Reduce stack size by allocating nsLineLayout objects from the heap when the frame tree depth gets too big (bug #10310)

This commit is contained in:
kipp%netscape.com 1999-08-27 21:45:37 +00:00
Родитель 424e55fe9d
Коммит e40963e370
8 изменённых файлов: 2060 добавлений и 1510 удалений

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIFocusTracker.h" #include "nsIFocusTracker.h"
#include "nsIFrameSelection.h" #include "nsIFrameSelection.h"
#define MAX_LINE_COUNT 50000
// XXX HTML:P's that are empty yet have style indicating they should // XXX HTML:P's that are empty yet have style indicating they should
// clear floaters - we need to ignore the clear behavior. // clear floaters - we need to ignore the clear behavior.
@ -203,8 +205,7 @@ public:
nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics);
nsLineLayout* aLineLayout);
~nsBlockReflowState(); ~nsBlockReflowState();
@ -240,9 +241,11 @@ public:
#endif #endif
} }
void InitFloater(nsPlaceholderFrame* aPlaceholderFrame); void InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame);
void AddFloater(nsPlaceholderFrame* aPlaceholderFrame, void AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame,
PRBool aInitialReflow); PRBool aInitialReflow);
PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats); PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats);
@ -302,13 +305,20 @@ public:
nscoord* aTopMarginResult, nscoord* aTopMarginResult,
nscoord* aBottomMarginResult); nscoord* aBottomMarginResult);
void ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult); void ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult);
void RecoverStateFrom(nsLineBox* aLine, void RecoverStateFrom(nsLineBox* aLine,
PRBool aApplyTopMargin, PRBool aApplyTopMargin,
nscoord aDeltaY, nscoord aDeltaY,
nsRect* aDamageRect); nsRect* aDamageRect);
void AdvanceToNextLine() {
mLineNumber++;
}
//---------------------------------------- //----------------------------------------
// This state is the "global" state computed once for the reflow of // This state is the "global" state computed once for the reflow of
@ -321,7 +331,7 @@ public:
const nsHTMLReflowState& mReflowState; const nsHTMLReflowState& mReflowState;
nsLineLayout* mLineLayout; // nsLineLayout* mLineLayout;
nsISpaceManager* mSpaceManager; nsISpaceManager* mSpaceManager;
@ -411,18 +421,22 @@ public:
PRBool mComputeMaxElementSize; PRBool mComputeMaxElementSize;
nsSize mMaxElementSize; nsSize mMaxElementSize;
nscoord mMinLineHeight;
PRInt32 mLineNumber;
}; };
// XXX This is vile. Make it go away // XXX This is vile. Make it go away
void void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame) nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->InitFloater(aFrame); mBlockRS->InitFloater(*this, aFrame);
} }
void void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->AddFloater(aFrame, PR_FALSE); mBlockRS->AddFloater(*this, aFrame, PR_FALSE);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -430,8 +444,7 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics)
nsLineLayout* aLineLayout)
: mBlock(aFrame), : mBlock(aFrame),
mPresContext(aPresContext), mPresContext(aPresContext),
mReflowState(aReflowState), mReflowState(aReflowState),
@ -439,10 +452,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
mIsBottomMarginRoot(PR_FALSE), mIsBottomMarginRoot(PR_FALSE),
mApplyTopMargin(PR_FALSE), mApplyTopMargin(PR_FALSE),
mNextRCFrame(nsnull), mNextRCFrame(nsnull),
mPrevBottomMargin(0) mPrevBottomMargin(0),
mLineNumber(0)
{ {
mLineLayout = aLineLayout;
mSpaceManager = aReflowState.mSpaceManager; mSpaceManager = aReflowState.mSpaceManager;
// Translate into our content area and then save the // Translate into our content area and then save the
@ -515,7 +527,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
break; break;
} }
mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;; mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;
mMaxElementSize.SizeTo(0, 0); mMaxElementSize.SizeTo(0, 0);
if (0 != borderPadding.top) { if (0 != borderPadding.top) {
@ -524,6 +536,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
if (0 != borderPadding.bottom) { if (0 != borderPadding.bottom) {
mIsBottomMarginRoot = PR_TRUE; mIsBottomMarginRoot = PR_TRUE;
} }
mMinLineHeight = nsHTMLReflowState::CalcLineHeight(*mPresContext,
aReflowState.frame);
} }
nsBlockReflowState::~nsBlockReflowState() nsBlockReflowState::~nsBlockReflowState()
@ -537,7 +552,9 @@ nsBlockReflowState::~nsBlockReflowState()
// at the current Y coordinate. This method assumes that // at the current Y coordinate. This method assumes that
// GetAvailableSpace has already been called. // GetAvailableSpace has already been called.
void void
nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType, nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult) nsRect& aResult)
{ {
nscoord availHeight = mUnconstrainedHeight nscoord availHeight = mUnconstrainedHeight
@ -546,7 +563,28 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
const nsMargin& borderPadding = BorderPadding(); const nsMargin& borderPadding = BorderPadding();
nscoord availX, availWidth; nscoord availX, availWidth;
if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
if (NS_STYLE_DISPLAY_LIST_ITEM == aDisplay->mDisplay) {
// XXX This is a hack until the css2 folks can figure how to deal
// with list-items and floaters.
nscoord leftMargin = 0;
#if 0
if (mAvailSpaceRect.x != borderPadding.left) {
// When a list-item is impacted by a left floater, slide it over
// by the right amount so that the bullets will (hopefully) be
// visible.
float p2t;
mPresContext->GetScaledPixelsToTwips(&p2t);
leftMargin = NSIntPixelsToTwips(40, p2t);
}
#endif
// Assume the frame is clueless about the space manager and only
// give it free space.
availX = mAvailSpaceRect.x + borderPadding.left + leftMargin;
availWidth = mAvailSpaceRect.width - leftMargin;
}
else if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
// Frames that know how to do non-rectangular splitting are given // Frames that know how to do non-rectangular splitting are given
// the entire available space, including space consumed by // the entire available space, including space consumed by
// floaters. // floaters.
@ -561,6 +599,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
availX = mAvailSpaceRect.x + borderPadding.left; availX = mAvailSpaceRect.x + borderPadding.left;
availWidth = mAvailSpaceRect.width; availWidth = mAvailSpaceRect.width;
} }
aResult.SetRect(availX, mY, availWidth, availHeight); aResult.SetRect(availX, mY, availWidth, availHeight);
} }
@ -630,10 +669,13 @@ nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine,
// Setup reflow state to compute the block childs top and bottom // Setup reflow state to compute the block childs top and bottom
// margins // margins
nsIFrame* frame = aLine->mFirstChild; nsIFrame* frame = aLine->mFirstChild;
nsRect availSpaceRect;
const nsStyleDisplay* display;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) display);
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpaceRect; ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect);
ComputeBlockAvailSpace(splitType, availSpaceRect);
nsSize availSpace(availSpaceRect.width, availSpaceRect.height); nsSize availSpace(availSpaceRect.width, availSpaceRect.height);
nsHTMLReflowState reflowState(*mPresContext, mReflowState, nsHTMLReflowState reflowState(*mPresContext, mReflowState,
frame, availSpace); frame, availSpace);
@ -1150,11 +1192,21 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState, const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus) nsReflowStatus& aStatus)
{ {
nsLineLayout lineLayout(aPresContext, aReflowState.mSpaceManager, if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
&aReflowState, nsnull != aMetrics.maxElementSize); #ifdef DEBUG_kipp
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics, {
&lineLayout); extern char* nsPresShell_ReflowStackPointerTop;
lineLayout.Init(&state); char marker;
char* newsp = (char*) ▮
printf("XXX: frame tree is too deep; approx stack size = %d\n",
nsPresShell_ReflowStackPointerTop - newsp);
}
#endif
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics);
if (NS_BLOCK_MARGIN_ROOT & mFlags) { if (NS_BLOCK_MARGIN_ROOT & mFlags) {
state.mIsTopMarginRoot = PR_TRUE; state.mIsTopMarginRoot = PR_TRUE;
state.mIsBottomMarginRoot = PR_TRUE; state.mIsBottomMarginRoot = PR_TRUE;
@ -1887,9 +1939,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE; PRBool keepGoing = PR_TRUE;
// Inform line layout of where the text runs are
aState.mLineLayout->SetReflowTextRuns(mTextRuns);
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
if (aState.mReflowState.reason == eReflowReason_Incremental) { if (aState.mReflowState.reason == eReflowReason_Incremental) {
nsIReflowCommand::ReflowType type; nsIReflowCommand::ReflowType type;
@ -1980,7 +2029,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
// Pull data from a next-in-flow if we can // Pull data from a next-in-flow if we can
@ -2049,13 +2098,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
} }
// Handle an odd-ball case: a list-item with no lines // Handle an odd-ball case: a list-item with no lines
if (mBullet && HaveOutsideBullet() && !mLines) { if (mBullet && HaveOutsideBullet() && !mLines) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
} }
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
@ -2289,8 +2343,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock"); NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock");
NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters"); NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters");
} }
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (0 != --fromLine->mChildCount) { if (0 != --fromLine->mChildCount) {
NS_ASSERTION(fromLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(fromLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
// Mark line dirty now that we pulled a child // Mark line dirty now that we pulled a child
fromLine->MarkDirty(); fromLine->MarkDirty();
frame->GetNextSibling(&fromLine->mFirstChild); frame->GetNextSibling(&fromLine->mFirstChild);
@ -2585,6 +2640,38 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
return PR_FALSE; return PR_FALSE;
} }
#if 0
const nsStyleText* styleText;
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
if ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)) {
// Since whitespace is significant, we know that the paragraph
// is not empty (even if it has no text in it because it has
return PR_FALSE;
static PRBool
IsEmptyHTMLParagraph(nsIFrame* aFrame)
{
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf)) &&
nsBlockReflowContext::IsHTMLParagraph(aFrame)) {
if (!bf->mLines) {
// It's an html paragraph and it's empty
return PR_TRUE;
}
nsLineBox* line = bf->mLines;
while (line) {
if (!IsEmptyLine(line)) {
return PR_FALSE;
}
line = line->mNext;
}
}
return PR_FALSE;
}
#endif
nsIFrame* nsIFrame*
nsBlockFrame::GetTopBlockChild() nsBlockFrame::GetTopBlockChild()
{ {
@ -2650,52 +2737,6 @@ nsBlockFrame::GetTopBlockChild()
return nsnull; return nsnull;
} }
static void ComputeCombinedArea(const nsRect aRect1, const nsRect aRect2, nsRect& aOutRect)
{
// Rect 1's top left point: (aRect.x, aRect1.y)
// Rect 2's top left point: (aRect2.x, aRect2.y)
// Rect 2's bottom right point: (x2, y2)
// Output rect's top left point: (aOutRect.x, aOutRect.y)
// Output rect's bottom right point: (xOut, yOut)
//
// Calculate the top left point of the output rect
//
// Initialize output rect's top left point to Rect 1's top left point
aOutRect.x = aRect1.x;
aOutRect.y = aRect1.y;
if (aRect2.x < aRect1.x) {
aOutRect.x = aRect2.x;
}
if (aRect2.y < aRect1.y) {
aOutRect.y = aRect2.y;
}
//
// Calculate the bottom right point of the output rect
//
// Initialize output rect's bottom right point to Rect 1's bottom right point
nscoord xOut = aRect1.x + aRect1.width;
nscoord yOut = aRect1.y + aRect1.height;
// Initialize Rect 2's bottom right point
nscoord x2 = aRect2.x + aRect2.width;
nscoord y2 = aRect2.y + aRect2.height;
if (x2 > xOut) {
xOut = x2;
}
if (y2 > yOut) {
yOut = y2;
}
//
// Set the width and height on the output rect.
//
aOutRect.width = xOut - aOutRect.x;
aOutRect.height = yOut - aOutRect.y;
}
nsresult nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsLineBox* aLine, nsLineBox* aLine,
@ -2739,7 +2780,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpace; nsRect availSpace;
aState.ComputeBlockAvailSpace(splitType, availSpace); aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
// Reflow the block into the available space // Reflow the block into the available space
nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
@ -2802,7 +2843,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// Do not count the continuation child on the line it used // Do not count the continuation child on the line it used
// to be on // to be on
aLine->mChildCount--; aLine->mChildCount--;
NS_ASSERTION(aLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line count");
} }
// Advance to next line since some of the block fit. That way // Advance to next line since some of the block fit. That way
@ -2885,11 +2926,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
bbox.y = aState.BorderPadding().top + ascent - bbox.y = aState.BorderPadding().top + ascent -
metrics.ascent + topMargin; metrics.ascent + topMargin;
mBullet->SetRect(bbox); mBullet->SetRect(bbox);
// Fix for bug 8314. Include the bullet's area in the combined area
// of the current line.
ComputeCombinedArea((const nsRect) bbox, (const nsRect) aLine->mCombinedArea,
aLine->mCombinedArea);
} }
} }
else { else {
@ -2929,134 +2965,197 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
#ifdef DEBUG #ifdef DEBUG
PRInt32 spins = 0; PRInt32 spins = 0;
#endif #endif
for (;;) { PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
if (nsnull != aLine->mFloaters) { while (LINE_REFLOW_REDO == lineReflowStatus) {
// Forget all of the floaters on the line // Prevent overflowing limited thread stacks by creating
aLine->mFloaters->Clear(); // nsLineLayout from the heap when the frame tree depth gets
} // large.
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0); if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
aState.mPendingFloaters.Clear(); rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
&lineReflowStatus);
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
} }
else { else {
/* XXX get the height right! */ rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
availHeight = aState.mAvailSpaceRect.height; &lineReflowStatus);
} }
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount(); if (NS_FAILED(rv)) {
nsLineLayout* lineLayout = aState.mLineLayout; break;
lineLayout->BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == lineLayout->GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
lineLayout->SetFirstLetterStyleOK(PR_TRUE);
} }
// Reflow the frames that are already on the line first
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
lineLayout->EndLineReflow();
#ifdef DEBUG #ifdef DEBUG
spins++; spins++;
if (1000 == spins) { if (1000 == spins) {
ListTag(stdout); ListTag(stdout);
printf(": yikes! spinning on a line over 1000 times!\n"); printf(": yikes! spinning on a line over 1000 times!\n");
NS_ABORT(); NS_ABORT();
}
#endif
continue;
} }
#endif
}
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout* ll = new nsLineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
if (!ll) {
return NS_ERROR_OUT_OF_MEMORY;
}
ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
ll->SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
aLineReflowStatus);
ll->EndLineReflow();
delete ll;
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout lineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
lineLayout.SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
aKeepReflowGoing, aLineReflowStatus);
lineLayout.EndLineReflow();
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
if (nsnull != aLine->mFloaters) {
// Forget all of the floaters on the line
aLine->mFloaters->Clear();
}
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
aState.mPendingFloaters.Clear();
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
}
else {
/* XXX get the height right! */
availHeight = aState.mAvailSpaceRect.height;
}
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount();
aLineLayout.BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == aLineLayout.GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
}
// Reflow the frames that are already on the line first
nsresult rv = NS_OK;
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
}
else {
// If we are propogating out a break-before status then there is // If we are propogating out a break-before status then there is
// no point in placing the line. // no point in placing the line.
NS_ASSERTION(aLine->mChildCount < 10000, "bad line child count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
lineLayout->EndLineReflow(); rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing);
} }
else {
rv = PlaceLine(aState, aLine, aKeepReflowGoing);
}
break;
} }
*aLineReflowStatus = lineReflowStatus;
return rv; return rv;
} }
@ -3071,6 +3170,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
*/ */
nsresult nsresult
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus) PRUint8* aLineReflowStatus)
@ -3079,7 +3179,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// If it's currently ok to be reflowing in first-letter style then // If it's currently ok to be reflowing in first-letter style then
// we must be about to reflow a frame that has first-letter style. // we must be about to reflow a frame that has first-letter style.
PRBool reflowingFirstLetter = aState.mLineLayout->GetFirstLetterStyleOK(); PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
#ifdef NOISY_FIRST_LETTER #ifdef NOISY_FIRST_LETTER
ListTag(stdout); ListTag(stdout);
printf(": reflowing "); printf(": reflowing ");
@ -3088,9 +3188,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
#endif #endif
// Reflow the inline frame // Reflow the inline frame
nsLineLayout* lineLayout = aState.mLineLayout;
nsReflowStatus frameReflowStatus; nsReflowStatus frameReflowStatus;
nsresult rv = lineLayout->ReflowFrame(aFrame, &aState.mNextRCFrame, nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
frameReflowStatus); frameReflowStatus);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -3135,7 +3234,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
else { else {
// It's not the first child on this line so go ahead and split // It's not the first child on this line so go ahead and split
// the line. We will see the frame again on the next-line. // the line. We will see the frame again on the next-line.
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3157,7 +3256,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line, but after the frame just reflowed // Split line, but after the frame just reflowed
nsIFrame* nextFrame; nsIFrame* nextFrame;
aFrame->GetNextSibling(&nextFrame); aFrame->GetNextSibling(&nextFrame);
rv = SplitLine(aState, aLine, nextFrame); rv = SplitLine(aState, aLineLayout, aLine, nextFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3203,7 +3302,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line after the current frame // Split line after the current frame
*aLineReflowStatus = LINE_REFLOW_STOP; *aLineReflowStatus = LINE_REFLOW_STOP;
aFrame->GetNextSibling(&aFrame); aFrame->GetNextSibling(&aFrame);
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3241,6 +3340,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
if (nsnull != nextInFlow) { if (nsnull != nextInFlow) {
aMadeNewFrame = PR_TRUE; aMadeNewFrame = PR_TRUE;
aLine->mChildCount++; aLine->mChildCount++;
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_FALSE); VerifyLines(PR_FALSE);
@ -3250,11 +3350,11 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
nsresult nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsBlockFrame::SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame) nsIFrame* aFrame)
{ {
nsLineLayout* lineLayout = aState.mLineLayout; PRInt32 pushCount = aLine->ChildCount() - aLineLayout.GetCurrentSpanCount();
PRInt32 pushCount = aLine->ChildCount() - lineLayout->GetCurrentSpanCount();
NS_ASSERTION(pushCount >= 0, "bad push count"); NS_ASSERTION(pushCount >= 0, "bad push count");
//printf("BEFORE (pushCount=%d):\n", pushCount); //printf("BEFORE (pushCount=%d):\n", pushCount);
//aLine->List(stdout, 0); //aLine->List(stdout, 0);
@ -3294,7 +3394,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState,
// Let line layout know that some frames are no longer part of its // Let line layout know that some frames are no longer part of its
// state. // state.
if (!aLine->IsBlock()) { if (!aLine->IsBlock()) {
lineLayout->SplitLineTo(aLine->ChildCount()); aLineLayout.SplitLineTo(aLine->ChildCount());
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_TRUE); VerifyLines(PR_TRUE);
@ -3340,6 +3440,7 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
nsresult nsresult
nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing) PRBool* aKeepReflowGoing)
{ {
@ -3360,15 +3461,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// method is used for placing a line of inline frames. If the rare // method is used for placing a line of inline frames. If the rare
// case is happening then the worst that will happen is that the // case is happening then the worst that will happen is that the
// bullet frame will be reflowed twice. // bullet frame will be reflowed twice.
nsLineLayout* lineLayout = aState.mLineLayout;
PRBool addedBullet = PR_FALSE; PRBool addedBullet = PR_FALSE;
if (HaveOutsideBullet() && (aLine == mLines) && if (HaveOutsideBullet() && (aLine == mLines) &&
(!lineLayout->IsZeroHeight() || !aLine->mNext)) { (!aLineLayout.IsZeroHeight() || !aLine->mNext)) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
aLineLayout.AddBulletFrame(mBullet, metrics);
addedBullet = PR_TRUE; addedBullet = PR_TRUE;
} }
nsSize maxElementSize; nsSize maxElementSize;
lineLayout->VerticalAlignFrames(aLine->mBounds, maxElementSize); aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize);
#ifdef DEBUG #ifdef DEBUG
{ {
static nscoord lastHeight = 0; static nscoord lastHeight = 0;
@ -3397,11 +3499,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
#else #else
PRBool allowJustify = PR_FALSE; PRBool allowJustify = PR_FALSE;
#endif #endif
lineLayout->TrimTrailingWhiteSpace(aLine->mBounds); aLineLayout.TrimTrailingWhiteSpace(aLine->mBounds);
lineLayout->HorizontalAlignFrames(aLine->mBounds, allowJustify); aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
lineLayout->RelativePositionFrames(aLine->mCombinedArea); aLineLayout.RelativePositionFrames(aLine->mCombinedArea);
if (addedBullet) { if (addedBullet) {
lineLayout->RemoveBulletFrame(mBullet); aLineLayout.RemoveBulletFrame(mBullet);
} }
// Inline lines do not have margins themselves; however they are // Inline lines do not have margins themselves; however they are
@ -3443,7 +3545,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
*aKeepReflowGoing = PR_FALSE; *aKeepReflowGoing = PR_FALSE;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -3491,8 +3592,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
break; break;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -4052,6 +4151,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
else { else {
prevSibLine->mChildCount++; prevSibLine->mChildCount++;
prevSibLine->MarkDirty(); prevSibLine->MarkDirty();
NS_ASSERTION(prevSibLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
aPrevSibling = newFrame; aPrevSibling = newFrame;
@ -4400,7 +4500,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
line = next; line = next;
} }
else { else {
NS_ASSERTION(line->mChildCount < 10000, "bad line count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line count");
// Make the line that just lost a frame dirty // Make the line that just lost a frame dirty
line->MarkDirty(); line->MarkDirty();
@ -4654,7 +4754,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
} }
void void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder) nsBlockReflowState::InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder)
{ {
// Set the geometric parent of the floater // Set the geometric parent of the floater
nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame(); nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
@ -4662,7 +4763,7 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// Then add the floater to the current line and place it when // Then add the floater to the current line and place it when
// appropriate // appropriate
AddFloater(aPlaceholder, PR_TRUE); AddFloater(aLineLayout, aPlaceholder, PR_TRUE);
} }
// This is called by the line layout's AddFloater method when a // This is called by the line layout's AddFloater method when a
@ -4671,7 +4772,8 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// then the floater is place immediately, otherwise the floater // then the floater is place immediately, otherwise the floater
// placement is deferred until the line has been reflowed. // placement is deferred until the line has been reflowed.
void void
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder, nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder,
PRBool aInitialReflow) PRBool aInitialReflow)
{ {
NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");
@ -4684,7 +4786,7 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Now place the floater immediately if possible. Otherwise stash it // Now place the floater immediately if possible. Otherwise stash it
// away in mPendingFloaters and place it later. // away in mPendingFloaters and place it later.
if (mLineLayout->CanPlaceFloaterNow()) { if (aLineLayout.CanPlaceFloaterNow()) {
nsRect combinedArea; nsRect combinedArea;
nsMargin floaterMargins; nsMargin floaterMargins;
nsMargin floaterOffsets; nsMargin floaterOffsets;
@ -4713,10 +4815,10 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Pass on updated available space to the current inline reflow engine // Pass on updated available space to the current inline reflow engine
GetAvailableSpace(); GetAvailableSpace();
mLineLayout->UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY,
mAvailSpaceRect.width, mAvailSpaceRect.width,
mAvailSpaceRect.height, mAvailSpaceRect.height,
isLeftFloater); isLeftFloater);
// Restore coordinate system // Restore coordinate system
mSpaceManager->Translate(dx, dy); mSpaceManager->Translate(dx, dy);
@ -5138,6 +5240,10 @@ nsBlockFrame::Paint(nsIPresContext& aPresContext,
const nsRect& aDirtyRect, const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer) nsFramePaintLayer aWhichLayer)
{ {
if (NS_FRAME_IS_UNFLOWABLE & mState) {
return NS_OK;
}
#ifdef NOISY_DAMAGE_REPAIR #ifdef NOISY_DAMAGE_REPAIR
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) { if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PRInt32 depth = GetDepth(); PRInt32 depth = GetDepth();
@ -5804,7 +5910,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
availSize.height = NS_UNCONSTRAINEDSIZE; availSize.height = NS_UNCONSTRAINEDSIZE;
nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState, nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState,
mBullet, availSize); mBullet, availSize);
reflowState.mLineLayout = aState.mLineLayout;
nsIHTMLReflow* htmlReflow; nsIHTMLReflow* htmlReflow;
nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
@ -5825,25 +5930,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
} }
void
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState)
{
// First reflow the bullet
nsLineLayout* lineLayout = aState.mLineLayout;
nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
if (mLines) {
// If we have at least one line then let line-layout position the
// bullet (this way the bullet is vertically aligned properly).
lineLayout->AddBulletFrame(mBullet, metrics);
}
else {
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
}
}
//XXX get rid of this -- its slow //XXX get rid of this -- its slow
void void
nsBlockFrame::BuildFloaterList() nsBlockFrame::BuildFloaterList()
@ -6054,7 +6140,7 @@ nsAnonymousBlockFrame::RemoveFirstFrame()
else { else {
// Remove frame from line and mark the line dirty // Remove frame from line and mark the line dirty
--line->mChildCount; --line->mChildCount;
NS_ASSERTION(line->mChildCount < 10000, "bad inline count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad inline count");
line->MarkDirty(); line->MarkDirty();
firstChild->GetNextSibling(&line->mFirstChild); firstChild->GetNextSibling(&line->mFirstChild);
} }
@ -6158,7 +6244,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
} }
} }
} }
NS_ASSERTION(line->mChildCount < 10000, "bad line child count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line child count");
count += line->mChildCount; count += line->mChildCount;
line = line->mNext; line = line->mNext;
} }

Просмотреть файл

@ -225,6 +225,7 @@ protected:
PRBool aKeepReflowGoing); PRBool aKeepReflowGoing);
nsresult PlaceLine(nsBlockReflowState& aState, nsresult PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing); PRBool* aKeepReflowGoing);
@ -258,7 +259,24 @@ protected:
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepLineGoing); PRBool* aKeepLineGoing);
nsresult DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus);
nsresult DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus);
nsresult DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus);
nsresult ReflowInlineFrame(nsBlockReflowState& aState, nsresult ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus); PRUint8* aLineReflowStatus);
@ -278,6 +296,7 @@ protected:
PRBool& aMadeNewFrame); PRBool& aMadeNewFrame);
nsresult SplitLine(nsBlockReflowState& aState, nsresult SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame); nsIFrame* aFrame);
@ -332,8 +351,6 @@ protected:
void ReflowBullet(nsBlockReflowState& aState, void ReflowBullet(nsBlockReflowState& aState,
nsHTMLReflowMetrics& aMetrics); nsHTMLReflowMetrics& aMetrics);
void PlaceBullet(nsBlockReflowState& aState);
//---------------------------------------- //----------------------------------------
nsIFrame* LastChild(); nsIFrame* LastChild();

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIFocusTracker.h" #include "nsIFocusTracker.h"
#include "nsIFrameSelection.h" #include "nsIFrameSelection.h"
#define MAX_LINE_COUNT 50000
// XXX HTML:P's that are empty yet have style indicating they should // XXX HTML:P's that are empty yet have style indicating they should
// clear floaters - we need to ignore the clear behavior. // clear floaters - we need to ignore the clear behavior.
@ -203,8 +205,7 @@ public:
nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics);
nsLineLayout* aLineLayout);
~nsBlockReflowState(); ~nsBlockReflowState();
@ -240,9 +241,11 @@ public:
#endif #endif
} }
void InitFloater(nsPlaceholderFrame* aPlaceholderFrame); void InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame);
void AddFloater(nsPlaceholderFrame* aPlaceholderFrame, void AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame,
PRBool aInitialReflow); PRBool aInitialReflow);
PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats); PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats);
@ -302,13 +305,20 @@ public:
nscoord* aTopMarginResult, nscoord* aTopMarginResult,
nscoord* aBottomMarginResult); nscoord* aBottomMarginResult);
void ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult); void ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult);
void RecoverStateFrom(nsLineBox* aLine, void RecoverStateFrom(nsLineBox* aLine,
PRBool aApplyTopMargin, PRBool aApplyTopMargin,
nscoord aDeltaY, nscoord aDeltaY,
nsRect* aDamageRect); nsRect* aDamageRect);
void AdvanceToNextLine() {
mLineNumber++;
}
//---------------------------------------- //----------------------------------------
// This state is the "global" state computed once for the reflow of // This state is the "global" state computed once for the reflow of
@ -321,7 +331,7 @@ public:
const nsHTMLReflowState& mReflowState; const nsHTMLReflowState& mReflowState;
nsLineLayout* mLineLayout; // nsLineLayout* mLineLayout;
nsISpaceManager* mSpaceManager; nsISpaceManager* mSpaceManager;
@ -411,18 +421,22 @@ public:
PRBool mComputeMaxElementSize; PRBool mComputeMaxElementSize;
nsSize mMaxElementSize; nsSize mMaxElementSize;
nscoord mMinLineHeight;
PRInt32 mLineNumber;
}; };
// XXX This is vile. Make it go away // XXX This is vile. Make it go away
void void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame) nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->InitFloater(aFrame); mBlockRS->InitFloater(*this, aFrame);
} }
void void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->AddFloater(aFrame, PR_FALSE); mBlockRS->AddFloater(*this, aFrame, PR_FALSE);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -430,8 +444,7 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics)
nsLineLayout* aLineLayout)
: mBlock(aFrame), : mBlock(aFrame),
mPresContext(aPresContext), mPresContext(aPresContext),
mReflowState(aReflowState), mReflowState(aReflowState),
@ -439,10 +452,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
mIsBottomMarginRoot(PR_FALSE), mIsBottomMarginRoot(PR_FALSE),
mApplyTopMargin(PR_FALSE), mApplyTopMargin(PR_FALSE),
mNextRCFrame(nsnull), mNextRCFrame(nsnull),
mPrevBottomMargin(0) mPrevBottomMargin(0),
mLineNumber(0)
{ {
mLineLayout = aLineLayout;
mSpaceManager = aReflowState.mSpaceManager; mSpaceManager = aReflowState.mSpaceManager;
// Translate into our content area and then save the // Translate into our content area and then save the
@ -515,7 +527,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
break; break;
} }
mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;; mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;
mMaxElementSize.SizeTo(0, 0); mMaxElementSize.SizeTo(0, 0);
if (0 != borderPadding.top) { if (0 != borderPadding.top) {
@ -524,6 +536,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
if (0 != borderPadding.bottom) { if (0 != borderPadding.bottom) {
mIsBottomMarginRoot = PR_TRUE; mIsBottomMarginRoot = PR_TRUE;
} }
mMinLineHeight = nsHTMLReflowState::CalcLineHeight(*mPresContext,
aReflowState.frame);
} }
nsBlockReflowState::~nsBlockReflowState() nsBlockReflowState::~nsBlockReflowState()
@ -537,7 +552,9 @@ nsBlockReflowState::~nsBlockReflowState()
// at the current Y coordinate. This method assumes that // at the current Y coordinate. This method assumes that
// GetAvailableSpace has already been called. // GetAvailableSpace has already been called.
void void
nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType, nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult) nsRect& aResult)
{ {
nscoord availHeight = mUnconstrainedHeight nscoord availHeight = mUnconstrainedHeight
@ -546,7 +563,28 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
const nsMargin& borderPadding = BorderPadding(); const nsMargin& borderPadding = BorderPadding();
nscoord availX, availWidth; nscoord availX, availWidth;
if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
if (NS_STYLE_DISPLAY_LIST_ITEM == aDisplay->mDisplay) {
// XXX This is a hack until the css2 folks can figure how to deal
// with list-items and floaters.
nscoord leftMargin = 0;
#if 0
if (mAvailSpaceRect.x != borderPadding.left) {
// When a list-item is impacted by a left floater, slide it over
// by the right amount so that the bullets will (hopefully) be
// visible.
float p2t;
mPresContext->GetScaledPixelsToTwips(&p2t);
leftMargin = NSIntPixelsToTwips(40, p2t);
}
#endif
// Assume the frame is clueless about the space manager and only
// give it free space.
availX = mAvailSpaceRect.x + borderPadding.left + leftMargin;
availWidth = mAvailSpaceRect.width - leftMargin;
}
else if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
// Frames that know how to do non-rectangular splitting are given // Frames that know how to do non-rectangular splitting are given
// the entire available space, including space consumed by // the entire available space, including space consumed by
// floaters. // floaters.
@ -561,6 +599,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
availX = mAvailSpaceRect.x + borderPadding.left; availX = mAvailSpaceRect.x + borderPadding.left;
availWidth = mAvailSpaceRect.width; availWidth = mAvailSpaceRect.width;
} }
aResult.SetRect(availX, mY, availWidth, availHeight); aResult.SetRect(availX, mY, availWidth, availHeight);
} }
@ -630,10 +669,13 @@ nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine,
// Setup reflow state to compute the block childs top and bottom // Setup reflow state to compute the block childs top and bottom
// margins // margins
nsIFrame* frame = aLine->mFirstChild; nsIFrame* frame = aLine->mFirstChild;
nsRect availSpaceRect;
const nsStyleDisplay* display;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) display);
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpaceRect; ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect);
ComputeBlockAvailSpace(splitType, availSpaceRect);
nsSize availSpace(availSpaceRect.width, availSpaceRect.height); nsSize availSpace(availSpaceRect.width, availSpaceRect.height);
nsHTMLReflowState reflowState(*mPresContext, mReflowState, nsHTMLReflowState reflowState(*mPresContext, mReflowState,
frame, availSpace); frame, availSpace);
@ -1150,11 +1192,21 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState, const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus) nsReflowStatus& aStatus)
{ {
nsLineLayout lineLayout(aPresContext, aReflowState.mSpaceManager, if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
&aReflowState, nsnull != aMetrics.maxElementSize); #ifdef DEBUG_kipp
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics, {
&lineLayout); extern char* nsPresShell_ReflowStackPointerTop;
lineLayout.Init(&state); char marker;
char* newsp = (char*) &marker;
printf("XXX: frame tree is too deep; approx stack size = %d\n",
nsPresShell_ReflowStackPointerTop - newsp);
}
#endif
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics);
if (NS_BLOCK_MARGIN_ROOT & mFlags) { if (NS_BLOCK_MARGIN_ROOT & mFlags) {
state.mIsTopMarginRoot = PR_TRUE; state.mIsTopMarginRoot = PR_TRUE;
state.mIsBottomMarginRoot = PR_TRUE; state.mIsBottomMarginRoot = PR_TRUE;
@ -1887,9 +1939,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE; PRBool keepGoing = PR_TRUE;
// Inform line layout of where the text runs are
aState.mLineLayout->SetReflowTextRuns(mTextRuns);
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
if (aState.mReflowState.reason == eReflowReason_Incremental) { if (aState.mReflowState.reason == eReflowReason_Incremental) {
nsIReflowCommand::ReflowType type; nsIReflowCommand::ReflowType type;
@ -1980,7 +2029,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
// Pull data from a next-in-flow if we can // Pull data from a next-in-flow if we can
@ -2049,13 +2098,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
} }
// Handle an odd-ball case: a list-item with no lines // Handle an odd-ball case: a list-item with no lines
if (mBullet && HaveOutsideBullet() && !mLines) { if (mBullet && HaveOutsideBullet() && !mLines) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
} }
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
@ -2289,8 +2343,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock"); NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock");
NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters"); NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters");
} }
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (0 != --fromLine->mChildCount) { if (0 != --fromLine->mChildCount) {
NS_ASSERTION(fromLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(fromLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
// Mark line dirty now that we pulled a child // Mark line dirty now that we pulled a child
fromLine->MarkDirty(); fromLine->MarkDirty();
frame->GetNextSibling(&fromLine->mFirstChild); frame->GetNextSibling(&fromLine->mFirstChild);
@ -2585,6 +2640,38 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
return PR_FALSE; return PR_FALSE;
} }
#if 0
const nsStyleText* styleText;
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
if ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)) {
// Since whitespace is significant, we know that the paragraph
// is not empty (even if it has no text in it because it has
return PR_FALSE;
static PRBool
IsEmptyHTMLParagraph(nsIFrame* aFrame)
{
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf)) &&
nsBlockReflowContext::IsHTMLParagraph(aFrame)) {
if (!bf->mLines) {
// It's an html paragraph and it's empty
return PR_TRUE;
}
nsLineBox* line = bf->mLines;
while (line) {
if (!IsEmptyLine(line)) {
return PR_FALSE;
}
line = line->mNext;
}
}
return PR_FALSE;
}
#endif
nsIFrame* nsIFrame*
nsBlockFrame::GetTopBlockChild() nsBlockFrame::GetTopBlockChild()
{ {
@ -2650,52 +2737,6 @@ nsBlockFrame::GetTopBlockChild()
return nsnull; return nsnull;
} }
static void ComputeCombinedArea(const nsRect aRect1, const nsRect aRect2, nsRect& aOutRect)
{
// Rect 1's top left point: (aRect.x, aRect1.y)
// Rect 2's top left point: (aRect2.x, aRect2.y)
// Rect 2's bottom right point: (x2, y2)
// Output rect's top left point: (aOutRect.x, aOutRect.y)
// Output rect's bottom right point: (xOut, yOut)
//
// Calculate the top left point of the output rect
//
// Initialize output rect's top left point to Rect 1's top left point
aOutRect.x = aRect1.x;
aOutRect.y = aRect1.y;
if (aRect2.x < aRect1.x) {
aOutRect.x = aRect2.x;
}
if (aRect2.y < aRect1.y) {
aOutRect.y = aRect2.y;
}
//
// Calculate the bottom right point of the output rect
//
// Initialize output rect's bottom right point to Rect 1's bottom right point
nscoord xOut = aRect1.x + aRect1.width;
nscoord yOut = aRect1.y + aRect1.height;
// Initialize Rect 2's bottom right point
nscoord x2 = aRect2.x + aRect2.width;
nscoord y2 = aRect2.y + aRect2.height;
if (x2 > xOut) {
xOut = x2;
}
if (y2 > yOut) {
yOut = y2;
}
//
// Set the width and height on the output rect.
//
aOutRect.width = xOut - aOutRect.x;
aOutRect.height = yOut - aOutRect.y;
}
nsresult nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsLineBox* aLine, nsLineBox* aLine,
@ -2739,7 +2780,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpace; nsRect availSpace;
aState.ComputeBlockAvailSpace(splitType, availSpace); aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
// Reflow the block into the available space // Reflow the block into the available space
nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
@ -2802,7 +2843,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// Do not count the continuation child on the line it used // Do not count the continuation child on the line it used
// to be on // to be on
aLine->mChildCount--; aLine->mChildCount--;
NS_ASSERTION(aLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line count");
} }
// Advance to next line since some of the block fit. That way // Advance to next line since some of the block fit. That way
@ -2885,11 +2926,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
bbox.y = aState.BorderPadding().top + ascent - bbox.y = aState.BorderPadding().top + ascent -
metrics.ascent + topMargin; metrics.ascent + topMargin;
mBullet->SetRect(bbox); mBullet->SetRect(bbox);
// Fix for bug 8314. Include the bullet's area in the combined area
// of the current line.
ComputeCombinedArea((const nsRect) bbox, (const nsRect) aLine->mCombinedArea,
aLine->mCombinedArea);
} }
} }
else { else {
@ -2929,134 +2965,197 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
#ifdef DEBUG #ifdef DEBUG
PRInt32 spins = 0; PRInt32 spins = 0;
#endif #endif
for (;;) { PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
if (nsnull != aLine->mFloaters) { while (LINE_REFLOW_REDO == lineReflowStatus) {
// Forget all of the floaters on the line // Prevent overflowing limited thread stacks by creating
aLine->mFloaters->Clear(); // nsLineLayout from the heap when the frame tree depth gets
} // large.
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0); if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
aState.mPendingFloaters.Clear(); rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
&lineReflowStatus);
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
} }
else { else {
/* XXX get the height right! */ rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
availHeight = aState.mAvailSpaceRect.height; &lineReflowStatus);
} }
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount(); if (NS_FAILED(rv)) {
nsLineLayout* lineLayout = aState.mLineLayout; break;
lineLayout->BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == lineLayout->GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
lineLayout->SetFirstLetterStyleOK(PR_TRUE);
} }
// Reflow the frames that are already on the line first
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
lineLayout->EndLineReflow();
#ifdef DEBUG #ifdef DEBUG
spins++; spins++;
if (1000 == spins) { if (1000 == spins) {
ListTag(stdout); ListTag(stdout);
printf(": yikes! spinning on a line over 1000 times!\n"); printf(": yikes! spinning on a line over 1000 times!\n");
NS_ABORT(); NS_ABORT();
}
#endif
continue;
} }
#endif
}
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout* ll = new nsLineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
if (!ll) {
return NS_ERROR_OUT_OF_MEMORY;
}
ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
ll->SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
aLineReflowStatus);
ll->EndLineReflow();
delete ll;
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout lineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
lineLayout.SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
aKeepReflowGoing, aLineReflowStatus);
lineLayout.EndLineReflow();
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
if (nsnull != aLine->mFloaters) {
// Forget all of the floaters on the line
aLine->mFloaters->Clear();
}
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
aState.mPendingFloaters.Clear();
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
}
else {
/* XXX get the height right! */
availHeight = aState.mAvailSpaceRect.height;
}
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount();
aLineLayout.BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == aLineLayout.GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
}
// Reflow the frames that are already on the line first
nsresult rv = NS_OK;
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
}
else {
// If we are propogating out a break-before status then there is // If we are propogating out a break-before status then there is
// no point in placing the line. // no point in placing the line.
NS_ASSERTION(aLine->mChildCount < 10000, "bad line child count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
lineLayout->EndLineReflow(); rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing);
} }
else {
rv = PlaceLine(aState, aLine, aKeepReflowGoing);
}
break;
} }
*aLineReflowStatus = lineReflowStatus;
return rv; return rv;
} }
@ -3071,6 +3170,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
*/ */
nsresult nsresult
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus) PRUint8* aLineReflowStatus)
@ -3079,7 +3179,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// If it's currently ok to be reflowing in first-letter style then // If it's currently ok to be reflowing in first-letter style then
// we must be about to reflow a frame that has first-letter style. // we must be about to reflow a frame that has first-letter style.
PRBool reflowingFirstLetter = aState.mLineLayout->GetFirstLetterStyleOK(); PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
#ifdef NOISY_FIRST_LETTER #ifdef NOISY_FIRST_LETTER
ListTag(stdout); ListTag(stdout);
printf(": reflowing "); printf(": reflowing ");
@ -3088,9 +3188,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
#endif #endif
// Reflow the inline frame // Reflow the inline frame
nsLineLayout* lineLayout = aState.mLineLayout;
nsReflowStatus frameReflowStatus; nsReflowStatus frameReflowStatus;
nsresult rv = lineLayout->ReflowFrame(aFrame, &aState.mNextRCFrame, nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
frameReflowStatus); frameReflowStatus);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -3135,7 +3234,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
else { else {
// It's not the first child on this line so go ahead and split // It's not the first child on this line so go ahead and split
// the line. We will see the frame again on the next-line. // the line. We will see the frame again on the next-line.
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3157,7 +3256,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line, but after the frame just reflowed // Split line, but after the frame just reflowed
nsIFrame* nextFrame; nsIFrame* nextFrame;
aFrame->GetNextSibling(&nextFrame); aFrame->GetNextSibling(&nextFrame);
rv = SplitLine(aState, aLine, nextFrame); rv = SplitLine(aState, aLineLayout, aLine, nextFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3203,7 +3302,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line after the current frame // Split line after the current frame
*aLineReflowStatus = LINE_REFLOW_STOP; *aLineReflowStatus = LINE_REFLOW_STOP;
aFrame->GetNextSibling(&aFrame); aFrame->GetNextSibling(&aFrame);
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3241,6 +3340,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
if (nsnull != nextInFlow) { if (nsnull != nextInFlow) {
aMadeNewFrame = PR_TRUE; aMadeNewFrame = PR_TRUE;
aLine->mChildCount++; aLine->mChildCount++;
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_FALSE); VerifyLines(PR_FALSE);
@ -3250,11 +3350,11 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
nsresult nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsBlockFrame::SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame) nsIFrame* aFrame)
{ {
nsLineLayout* lineLayout = aState.mLineLayout; PRInt32 pushCount = aLine->ChildCount() - aLineLayout.GetCurrentSpanCount();
PRInt32 pushCount = aLine->ChildCount() - lineLayout->GetCurrentSpanCount();
NS_ASSERTION(pushCount >= 0, "bad push count"); NS_ASSERTION(pushCount >= 0, "bad push count");
//printf("BEFORE (pushCount=%d):\n", pushCount); //printf("BEFORE (pushCount=%d):\n", pushCount);
//aLine->List(stdout, 0); //aLine->List(stdout, 0);
@ -3294,7 +3394,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState,
// Let line layout know that some frames are no longer part of its // Let line layout know that some frames are no longer part of its
// state. // state.
if (!aLine->IsBlock()) { if (!aLine->IsBlock()) {
lineLayout->SplitLineTo(aLine->ChildCount()); aLineLayout.SplitLineTo(aLine->ChildCount());
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_TRUE); VerifyLines(PR_TRUE);
@ -3340,6 +3440,7 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
nsresult nsresult
nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing) PRBool* aKeepReflowGoing)
{ {
@ -3360,15 +3461,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// method is used for placing a line of inline frames. If the rare // method is used for placing a line of inline frames. If the rare
// case is happening then the worst that will happen is that the // case is happening then the worst that will happen is that the
// bullet frame will be reflowed twice. // bullet frame will be reflowed twice.
nsLineLayout* lineLayout = aState.mLineLayout;
PRBool addedBullet = PR_FALSE; PRBool addedBullet = PR_FALSE;
if (HaveOutsideBullet() && (aLine == mLines) && if (HaveOutsideBullet() && (aLine == mLines) &&
(!lineLayout->IsZeroHeight() || !aLine->mNext)) { (!aLineLayout.IsZeroHeight() || !aLine->mNext)) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
aLineLayout.AddBulletFrame(mBullet, metrics);
addedBullet = PR_TRUE; addedBullet = PR_TRUE;
} }
nsSize maxElementSize; nsSize maxElementSize;
lineLayout->VerticalAlignFrames(aLine->mBounds, maxElementSize); aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize);
#ifdef DEBUG #ifdef DEBUG
{ {
static nscoord lastHeight = 0; static nscoord lastHeight = 0;
@ -3397,11 +3499,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
#else #else
PRBool allowJustify = PR_FALSE; PRBool allowJustify = PR_FALSE;
#endif #endif
lineLayout->TrimTrailingWhiteSpace(aLine->mBounds); aLineLayout.TrimTrailingWhiteSpace(aLine->mBounds);
lineLayout->HorizontalAlignFrames(aLine->mBounds, allowJustify); aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
lineLayout->RelativePositionFrames(aLine->mCombinedArea); aLineLayout.RelativePositionFrames(aLine->mCombinedArea);
if (addedBullet) { if (addedBullet) {
lineLayout->RemoveBulletFrame(mBullet); aLineLayout.RemoveBulletFrame(mBullet);
} }
// Inline lines do not have margins themselves; however they are // Inline lines do not have margins themselves; however they are
@ -3443,7 +3545,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
*aKeepReflowGoing = PR_FALSE; *aKeepReflowGoing = PR_FALSE;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -3491,8 +3592,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
break; break;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -4052,6 +4151,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
else { else {
prevSibLine->mChildCount++; prevSibLine->mChildCount++;
prevSibLine->MarkDirty(); prevSibLine->MarkDirty();
NS_ASSERTION(prevSibLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
aPrevSibling = newFrame; aPrevSibling = newFrame;
@ -4400,7 +4500,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
line = next; line = next;
} }
else { else {
NS_ASSERTION(line->mChildCount < 10000, "bad line count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line count");
// Make the line that just lost a frame dirty // Make the line that just lost a frame dirty
line->MarkDirty(); line->MarkDirty();
@ -4654,7 +4754,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
} }
void void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder) nsBlockReflowState::InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder)
{ {
// Set the geometric parent of the floater // Set the geometric parent of the floater
nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame(); nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
@ -4662,7 +4763,7 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// Then add the floater to the current line and place it when // Then add the floater to the current line and place it when
// appropriate // appropriate
AddFloater(aPlaceholder, PR_TRUE); AddFloater(aLineLayout, aPlaceholder, PR_TRUE);
} }
// This is called by the line layout's AddFloater method when a // This is called by the line layout's AddFloater method when a
@ -4671,7 +4772,8 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// then the floater is place immediately, otherwise the floater // then the floater is place immediately, otherwise the floater
// placement is deferred until the line has been reflowed. // placement is deferred until the line has been reflowed.
void void
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder, nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder,
PRBool aInitialReflow) PRBool aInitialReflow)
{ {
NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");
@ -4684,7 +4786,7 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Now place the floater immediately if possible. Otherwise stash it // Now place the floater immediately if possible. Otherwise stash it
// away in mPendingFloaters and place it later. // away in mPendingFloaters and place it later.
if (mLineLayout->CanPlaceFloaterNow()) { if (aLineLayout.CanPlaceFloaterNow()) {
nsRect combinedArea; nsRect combinedArea;
nsMargin floaterMargins; nsMargin floaterMargins;
nsMargin floaterOffsets; nsMargin floaterOffsets;
@ -4713,10 +4815,10 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Pass on updated available space to the current inline reflow engine // Pass on updated available space to the current inline reflow engine
GetAvailableSpace(); GetAvailableSpace();
mLineLayout->UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY,
mAvailSpaceRect.width, mAvailSpaceRect.width,
mAvailSpaceRect.height, mAvailSpaceRect.height,
isLeftFloater); isLeftFloater);
// Restore coordinate system // Restore coordinate system
mSpaceManager->Translate(dx, dy); mSpaceManager->Translate(dx, dy);
@ -5138,6 +5240,10 @@ nsBlockFrame::Paint(nsIPresContext& aPresContext,
const nsRect& aDirtyRect, const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer) nsFramePaintLayer aWhichLayer)
{ {
if (NS_FRAME_IS_UNFLOWABLE & mState) {
return NS_OK;
}
#ifdef NOISY_DAMAGE_REPAIR #ifdef NOISY_DAMAGE_REPAIR
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) { if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PRInt32 depth = GetDepth(); PRInt32 depth = GetDepth();
@ -5804,7 +5910,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
availSize.height = NS_UNCONSTRAINEDSIZE; availSize.height = NS_UNCONSTRAINEDSIZE;
nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState, nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState,
mBullet, availSize); mBullet, availSize);
reflowState.mLineLayout = aState.mLineLayout;
nsIHTMLReflow* htmlReflow; nsIHTMLReflow* htmlReflow;
nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
@ -5825,25 +5930,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
} }
void
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState)
{
// First reflow the bullet
nsLineLayout* lineLayout = aState.mLineLayout;
nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
if (mLines) {
// If we have at least one line then let line-layout position the
// bullet (this way the bullet is vertically aligned properly).
lineLayout->AddBulletFrame(mBullet, metrics);
}
else {
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
}
}
//XXX get rid of this -- its slow //XXX get rid of this -- its slow
void void
nsBlockFrame::BuildFloaterList() nsBlockFrame::BuildFloaterList()
@ -6054,7 +6140,7 @@ nsAnonymousBlockFrame::RemoveFirstFrame()
else { else {
// Remove frame from line and mark the line dirty // Remove frame from line and mark the line dirty
--line->mChildCount; --line->mChildCount;
NS_ASSERTION(line->mChildCount < 10000, "bad inline count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad inline count");
line->MarkDirty(); line->MarkDirty();
firstChild->GetNextSibling(&line->mFirstChild); firstChild->GetNextSibling(&line->mFirstChild);
} }
@ -6158,7 +6244,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
} }
} }
} }
NS_ASSERTION(line->mChildCount < 10000, "bad line child count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line child count");
count += line->mChildCount; count += line->mChildCount;
line = line->mNext; line = line->mNext;
} }

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIFocusTracker.h" #include "nsIFocusTracker.h"
#include "nsIFrameSelection.h" #include "nsIFrameSelection.h"
#define MAX_LINE_COUNT 50000
// XXX HTML:P's that are empty yet have style indicating they should // XXX HTML:P's that are empty yet have style indicating they should
// clear floaters - we need to ignore the clear behavior. // clear floaters - we need to ignore the clear behavior.
@ -203,8 +205,7 @@ public:
nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics);
nsLineLayout* aLineLayout);
~nsBlockReflowState(); ~nsBlockReflowState();
@ -240,9 +241,11 @@ public:
#endif #endif
} }
void InitFloater(nsPlaceholderFrame* aPlaceholderFrame); void InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame);
void AddFloater(nsPlaceholderFrame* aPlaceholderFrame, void AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame,
PRBool aInitialReflow); PRBool aInitialReflow);
PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats); PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats);
@ -302,13 +305,20 @@ public:
nscoord* aTopMarginResult, nscoord* aTopMarginResult,
nscoord* aBottomMarginResult); nscoord* aBottomMarginResult);
void ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult); void ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult);
void RecoverStateFrom(nsLineBox* aLine, void RecoverStateFrom(nsLineBox* aLine,
PRBool aApplyTopMargin, PRBool aApplyTopMargin,
nscoord aDeltaY, nscoord aDeltaY,
nsRect* aDamageRect); nsRect* aDamageRect);
void AdvanceToNextLine() {
mLineNumber++;
}
//---------------------------------------- //----------------------------------------
// This state is the "global" state computed once for the reflow of // This state is the "global" state computed once for the reflow of
@ -321,7 +331,7 @@ public:
const nsHTMLReflowState& mReflowState; const nsHTMLReflowState& mReflowState;
nsLineLayout* mLineLayout; // nsLineLayout* mLineLayout;
nsISpaceManager* mSpaceManager; nsISpaceManager* mSpaceManager;
@ -411,18 +421,22 @@ public:
PRBool mComputeMaxElementSize; PRBool mComputeMaxElementSize;
nsSize mMaxElementSize; nsSize mMaxElementSize;
nscoord mMinLineHeight;
PRInt32 mLineNumber;
}; };
// XXX This is vile. Make it go away // XXX This is vile. Make it go away
void void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame) nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->InitFloater(aFrame); mBlockRS->InitFloater(*this, aFrame);
} }
void void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->AddFloater(aFrame, PR_FALSE); mBlockRS->AddFloater(*this, aFrame, PR_FALSE);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -430,8 +444,7 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics)
nsLineLayout* aLineLayout)
: mBlock(aFrame), : mBlock(aFrame),
mPresContext(aPresContext), mPresContext(aPresContext),
mReflowState(aReflowState), mReflowState(aReflowState),
@ -439,10 +452,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
mIsBottomMarginRoot(PR_FALSE), mIsBottomMarginRoot(PR_FALSE),
mApplyTopMargin(PR_FALSE), mApplyTopMargin(PR_FALSE),
mNextRCFrame(nsnull), mNextRCFrame(nsnull),
mPrevBottomMargin(0) mPrevBottomMargin(0),
mLineNumber(0)
{ {
mLineLayout = aLineLayout;
mSpaceManager = aReflowState.mSpaceManager; mSpaceManager = aReflowState.mSpaceManager;
// Translate into our content area and then save the // Translate into our content area and then save the
@ -515,7 +527,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
break; break;
} }
mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;; mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;
mMaxElementSize.SizeTo(0, 0); mMaxElementSize.SizeTo(0, 0);
if (0 != borderPadding.top) { if (0 != borderPadding.top) {
@ -524,6 +536,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
if (0 != borderPadding.bottom) { if (0 != borderPadding.bottom) {
mIsBottomMarginRoot = PR_TRUE; mIsBottomMarginRoot = PR_TRUE;
} }
mMinLineHeight = nsHTMLReflowState::CalcLineHeight(*mPresContext,
aReflowState.frame);
} }
nsBlockReflowState::~nsBlockReflowState() nsBlockReflowState::~nsBlockReflowState()
@ -537,7 +552,9 @@ nsBlockReflowState::~nsBlockReflowState()
// at the current Y coordinate. This method assumes that // at the current Y coordinate. This method assumes that
// GetAvailableSpace has already been called. // GetAvailableSpace has already been called.
void void
nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType, nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult) nsRect& aResult)
{ {
nscoord availHeight = mUnconstrainedHeight nscoord availHeight = mUnconstrainedHeight
@ -546,7 +563,28 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
const nsMargin& borderPadding = BorderPadding(); const nsMargin& borderPadding = BorderPadding();
nscoord availX, availWidth; nscoord availX, availWidth;
if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
if (NS_STYLE_DISPLAY_LIST_ITEM == aDisplay->mDisplay) {
// XXX This is a hack until the css2 folks can figure how to deal
// with list-items and floaters.
nscoord leftMargin = 0;
#if 0
if (mAvailSpaceRect.x != borderPadding.left) {
// When a list-item is impacted by a left floater, slide it over
// by the right amount so that the bullets will (hopefully) be
// visible.
float p2t;
mPresContext->GetScaledPixelsToTwips(&p2t);
leftMargin = NSIntPixelsToTwips(40, p2t);
}
#endif
// Assume the frame is clueless about the space manager and only
// give it free space.
availX = mAvailSpaceRect.x + borderPadding.left + leftMargin;
availWidth = mAvailSpaceRect.width - leftMargin;
}
else if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
// Frames that know how to do non-rectangular splitting are given // Frames that know how to do non-rectangular splitting are given
// the entire available space, including space consumed by // the entire available space, including space consumed by
// floaters. // floaters.
@ -561,6 +599,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
availX = mAvailSpaceRect.x + borderPadding.left; availX = mAvailSpaceRect.x + borderPadding.left;
availWidth = mAvailSpaceRect.width; availWidth = mAvailSpaceRect.width;
} }
aResult.SetRect(availX, mY, availWidth, availHeight); aResult.SetRect(availX, mY, availWidth, availHeight);
} }
@ -630,10 +669,13 @@ nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine,
// Setup reflow state to compute the block childs top and bottom // Setup reflow state to compute the block childs top and bottom
// margins // margins
nsIFrame* frame = aLine->mFirstChild; nsIFrame* frame = aLine->mFirstChild;
nsRect availSpaceRect;
const nsStyleDisplay* display;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) display);
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpaceRect; ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect);
ComputeBlockAvailSpace(splitType, availSpaceRect);
nsSize availSpace(availSpaceRect.width, availSpaceRect.height); nsSize availSpace(availSpaceRect.width, availSpaceRect.height);
nsHTMLReflowState reflowState(*mPresContext, mReflowState, nsHTMLReflowState reflowState(*mPresContext, mReflowState,
frame, availSpace); frame, availSpace);
@ -1150,11 +1192,21 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState, const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus) nsReflowStatus& aStatus)
{ {
nsLineLayout lineLayout(aPresContext, aReflowState.mSpaceManager, if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
&aReflowState, nsnull != aMetrics.maxElementSize); #ifdef DEBUG_kipp
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics, {
&lineLayout); extern char* nsPresShell_ReflowStackPointerTop;
lineLayout.Init(&state); char marker;
char* newsp = (char*) &marker;
printf("XXX: frame tree is too deep; approx stack size = %d\n",
nsPresShell_ReflowStackPointerTop - newsp);
}
#endif
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics);
if (NS_BLOCK_MARGIN_ROOT & mFlags) { if (NS_BLOCK_MARGIN_ROOT & mFlags) {
state.mIsTopMarginRoot = PR_TRUE; state.mIsTopMarginRoot = PR_TRUE;
state.mIsBottomMarginRoot = PR_TRUE; state.mIsBottomMarginRoot = PR_TRUE;
@ -1887,9 +1939,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE; PRBool keepGoing = PR_TRUE;
// Inform line layout of where the text runs are
aState.mLineLayout->SetReflowTextRuns(mTextRuns);
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
if (aState.mReflowState.reason == eReflowReason_Incremental) { if (aState.mReflowState.reason == eReflowReason_Incremental) {
nsIReflowCommand::ReflowType type; nsIReflowCommand::ReflowType type;
@ -1980,7 +2029,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
// Pull data from a next-in-flow if we can // Pull data from a next-in-flow if we can
@ -2049,13 +2098,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
} }
// Handle an odd-ball case: a list-item with no lines // Handle an odd-ball case: a list-item with no lines
if (mBullet && HaveOutsideBullet() && !mLines) { if (mBullet && HaveOutsideBullet() && !mLines) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
} }
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
@ -2289,8 +2343,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock"); NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock");
NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters"); NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters");
} }
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (0 != --fromLine->mChildCount) { if (0 != --fromLine->mChildCount) {
NS_ASSERTION(fromLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(fromLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
// Mark line dirty now that we pulled a child // Mark line dirty now that we pulled a child
fromLine->MarkDirty(); fromLine->MarkDirty();
frame->GetNextSibling(&fromLine->mFirstChild); frame->GetNextSibling(&fromLine->mFirstChild);
@ -2585,6 +2640,38 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
return PR_FALSE; return PR_FALSE;
} }
#if 0
const nsStyleText* styleText;
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
if ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)) {
// Since whitespace is significant, we know that the paragraph
// is not empty (even if it has no text in it because it has
return PR_FALSE;
static PRBool
IsEmptyHTMLParagraph(nsIFrame* aFrame)
{
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf)) &&
nsBlockReflowContext::IsHTMLParagraph(aFrame)) {
if (!bf->mLines) {
// It's an html paragraph and it's empty
return PR_TRUE;
}
nsLineBox* line = bf->mLines;
while (line) {
if (!IsEmptyLine(line)) {
return PR_FALSE;
}
line = line->mNext;
}
}
return PR_FALSE;
}
#endif
nsIFrame* nsIFrame*
nsBlockFrame::GetTopBlockChild() nsBlockFrame::GetTopBlockChild()
{ {
@ -2650,52 +2737,6 @@ nsBlockFrame::GetTopBlockChild()
return nsnull; return nsnull;
} }
static void ComputeCombinedArea(const nsRect aRect1, const nsRect aRect2, nsRect& aOutRect)
{
// Rect 1's top left point: (aRect.x, aRect1.y)
// Rect 2's top left point: (aRect2.x, aRect2.y)
// Rect 2's bottom right point: (x2, y2)
// Output rect's top left point: (aOutRect.x, aOutRect.y)
// Output rect's bottom right point: (xOut, yOut)
//
// Calculate the top left point of the output rect
//
// Initialize output rect's top left point to Rect 1's top left point
aOutRect.x = aRect1.x;
aOutRect.y = aRect1.y;
if (aRect2.x < aRect1.x) {
aOutRect.x = aRect2.x;
}
if (aRect2.y < aRect1.y) {
aOutRect.y = aRect2.y;
}
//
// Calculate the bottom right point of the output rect
//
// Initialize output rect's bottom right point to Rect 1's bottom right point
nscoord xOut = aRect1.x + aRect1.width;
nscoord yOut = aRect1.y + aRect1.height;
// Initialize Rect 2's bottom right point
nscoord x2 = aRect2.x + aRect2.width;
nscoord y2 = aRect2.y + aRect2.height;
if (x2 > xOut) {
xOut = x2;
}
if (y2 > yOut) {
yOut = y2;
}
//
// Set the width and height on the output rect.
//
aOutRect.width = xOut - aOutRect.x;
aOutRect.height = yOut - aOutRect.y;
}
nsresult nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsLineBox* aLine, nsLineBox* aLine,
@ -2739,7 +2780,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpace; nsRect availSpace;
aState.ComputeBlockAvailSpace(splitType, availSpace); aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
// Reflow the block into the available space // Reflow the block into the available space
nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
@ -2802,7 +2843,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// Do not count the continuation child on the line it used // Do not count the continuation child on the line it used
// to be on // to be on
aLine->mChildCount--; aLine->mChildCount--;
NS_ASSERTION(aLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line count");
} }
// Advance to next line since some of the block fit. That way // Advance to next line since some of the block fit. That way
@ -2885,11 +2926,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
bbox.y = aState.BorderPadding().top + ascent - bbox.y = aState.BorderPadding().top + ascent -
metrics.ascent + topMargin; metrics.ascent + topMargin;
mBullet->SetRect(bbox); mBullet->SetRect(bbox);
// Fix for bug 8314. Include the bullet's area in the combined area
// of the current line.
ComputeCombinedArea((const nsRect) bbox, (const nsRect) aLine->mCombinedArea,
aLine->mCombinedArea);
} }
} }
else { else {
@ -2929,134 +2965,197 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
#ifdef DEBUG #ifdef DEBUG
PRInt32 spins = 0; PRInt32 spins = 0;
#endif #endif
for (;;) { PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
if (nsnull != aLine->mFloaters) { while (LINE_REFLOW_REDO == lineReflowStatus) {
// Forget all of the floaters on the line // Prevent overflowing limited thread stacks by creating
aLine->mFloaters->Clear(); // nsLineLayout from the heap when the frame tree depth gets
} // large.
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0); if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
aState.mPendingFloaters.Clear(); rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
&lineReflowStatus);
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
} }
else { else {
/* XXX get the height right! */ rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
availHeight = aState.mAvailSpaceRect.height; &lineReflowStatus);
} }
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount(); if (NS_FAILED(rv)) {
nsLineLayout* lineLayout = aState.mLineLayout; break;
lineLayout->BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == lineLayout->GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
lineLayout->SetFirstLetterStyleOK(PR_TRUE);
} }
// Reflow the frames that are already on the line first
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
lineLayout->EndLineReflow();
#ifdef DEBUG #ifdef DEBUG
spins++; spins++;
if (1000 == spins) { if (1000 == spins) {
ListTag(stdout); ListTag(stdout);
printf(": yikes! spinning on a line over 1000 times!\n"); printf(": yikes! spinning on a line over 1000 times!\n");
NS_ABORT(); NS_ABORT();
}
#endif
continue;
} }
#endif
}
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout* ll = new nsLineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
if (!ll) {
return NS_ERROR_OUT_OF_MEMORY;
}
ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
ll->SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
aLineReflowStatus);
ll->EndLineReflow();
delete ll;
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout lineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
lineLayout.SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
aKeepReflowGoing, aLineReflowStatus);
lineLayout.EndLineReflow();
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
if (nsnull != aLine->mFloaters) {
// Forget all of the floaters on the line
aLine->mFloaters->Clear();
}
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
aState.mPendingFloaters.Clear();
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
}
else {
/* XXX get the height right! */
availHeight = aState.mAvailSpaceRect.height;
}
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount();
aLineLayout.BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == aLineLayout.GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
}
// Reflow the frames that are already on the line first
nsresult rv = NS_OK;
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
}
else {
// If we are propogating out a break-before status then there is // If we are propogating out a break-before status then there is
// no point in placing the line. // no point in placing the line.
NS_ASSERTION(aLine->mChildCount < 10000, "bad line child count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
lineLayout->EndLineReflow(); rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing);
} }
else {
rv = PlaceLine(aState, aLine, aKeepReflowGoing);
}
break;
} }
*aLineReflowStatus = lineReflowStatus;
return rv; return rv;
} }
@ -3071,6 +3170,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
*/ */
nsresult nsresult
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus) PRUint8* aLineReflowStatus)
@ -3079,7 +3179,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// If it's currently ok to be reflowing in first-letter style then // If it's currently ok to be reflowing in first-letter style then
// we must be about to reflow a frame that has first-letter style. // we must be about to reflow a frame that has first-letter style.
PRBool reflowingFirstLetter = aState.mLineLayout->GetFirstLetterStyleOK(); PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
#ifdef NOISY_FIRST_LETTER #ifdef NOISY_FIRST_LETTER
ListTag(stdout); ListTag(stdout);
printf(": reflowing "); printf(": reflowing ");
@ -3088,9 +3188,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
#endif #endif
// Reflow the inline frame // Reflow the inline frame
nsLineLayout* lineLayout = aState.mLineLayout;
nsReflowStatus frameReflowStatus; nsReflowStatus frameReflowStatus;
nsresult rv = lineLayout->ReflowFrame(aFrame, &aState.mNextRCFrame, nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
frameReflowStatus); frameReflowStatus);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -3135,7 +3234,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
else { else {
// It's not the first child on this line so go ahead and split // It's not the first child on this line so go ahead and split
// the line. We will see the frame again on the next-line. // the line. We will see the frame again on the next-line.
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3157,7 +3256,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line, but after the frame just reflowed // Split line, but after the frame just reflowed
nsIFrame* nextFrame; nsIFrame* nextFrame;
aFrame->GetNextSibling(&nextFrame); aFrame->GetNextSibling(&nextFrame);
rv = SplitLine(aState, aLine, nextFrame); rv = SplitLine(aState, aLineLayout, aLine, nextFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3203,7 +3302,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line after the current frame // Split line after the current frame
*aLineReflowStatus = LINE_REFLOW_STOP; *aLineReflowStatus = LINE_REFLOW_STOP;
aFrame->GetNextSibling(&aFrame); aFrame->GetNextSibling(&aFrame);
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3241,6 +3340,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
if (nsnull != nextInFlow) { if (nsnull != nextInFlow) {
aMadeNewFrame = PR_TRUE; aMadeNewFrame = PR_TRUE;
aLine->mChildCount++; aLine->mChildCount++;
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_FALSE); VerifyLines(PR_FALSE);
@ -3250,11 +3350,11 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
nsresult nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsBlockFrame::SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame) nsIFrame* aFrame)
{ {
nsLineLayout* lineLayout = aState.mLineLayout; PRInt32 pushCount = aLine->ChildCount() - aLineLayout.GetCurrentSpanCount();
PRInt32 pushCount = aLine->ChildCount() - lineLayout->GetCurrentSpanCount();
NS_ASSERTION(pushCount >= 0, "bad push count"); NS_ASSERTION(pushCount >= 0, "bad push count");
//printf("BEFORE (pushCount=%d):\n", pushCount); //printf("BEFORE (pushCount=%d):\n", pushCount);
//aLine->List(stdout, 0); //aLine->List(stdout, 0);
@ -3294,7 +3394,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState,
// Let line layout know that some frames are no longer part of its // Let line layout know that some frames are no longer part of its
// state. // state.
if (!aLine->IsBlock()) { if (!aLine->IsBlock()) {
lineLayout->SplitLineTo(aLine->ChildCount()); aLineLayout.SplitLineTo(aLine->ChildCount());
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_TRUE); VerifyLines(PR_TRUE);
@ -3340,6 +3440,7 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
nsresult nsresult
nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing) PRBool* aKeepReflowGoing)
{ {
@ -3360,15 +3461,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// method is used for placing a line of inline frames. If the rare // method is used for placing a line of inline frames. If the rare
// case is happening then the worst that will happen is that the // case is happening then the worst that will happen is that the
// bullet frame will be reflowed twice. // bullet frame will be reflowed twice.
nsLineLayout* lineLayout = aState.mLineLayout;
PRBool addedBullet = PR_FALSE; PRBool addedBullet = PR_FALSE;
if (HaveOutsideBullet() && (aLine == mLines) && if (HaveOutsideBullet() && (aLine == mLines) &&
(!lineLayout->IsZeroHeight() || !aLine->mNext)) { (!aLineLayout.IsZeroHeight() || !aLine->mNext)) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
aLineLayout.AddBulletFrame(mBullet, metrics);
addedBullet = PR_TRUE; addedBullet = PR_TRUE;
} }
nsSize maxElementSize; nsSize maxElementSize;
lineLayout->VerticalAlignFrames(aLine->mBounds, maxElementSize); aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize);
#ifdef DEBUG #ifdef DEBUG
{ {
static nscoord lastHeight = 0; static nscoord lastHeight = 0;
@ -3397,11 +3499,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
#else #else
PRBool allowJustify = PR_FALSE; PRBool allowJustify = PR_FALSE;
#endif #endif
lineLayout->TrimTrailingWhiteSpace(aLine->mBounds); aLineLayout.TrimTrailingWhiteSpace(aLine->mBounds);
lineLayout->HorizontalAlignFrames(aLine->mBounds, allowJustify); aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
lineLayout->RelativePositionFrames(aLine->mCombinedArea); aLineLayout.RelativePositionFrames(aLine->mCombinedArea);
if (addedBullet) { if (addedBullet) {
lineLayout->RemoveBulletFrame(mBullet); aLineLayout.RemoveBulletFrame(mBullet);
} }
// Inline lines do not have margins themselves; however they are // Inline lines do not have margins themselves; however they are
@ -3443,7 +3545,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
*aKeepReflowGoing = PR_FALSE; *aKeepReflowGoing = PR_FALSE;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -3491,8 +3592,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
break; break;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -4052,6 +4151,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
else { else {
prevSibLine->mChildCount++; prevSibLine->mChildCount++;
prevSibLine->MarkDirty(); prevSibLine->MarkDirty();
NS_ASSERTION(prevSibLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
aPrevSibling = newFrame; aPrevSibling = newFrame;
@ -4400,7 +4500,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
line = next; line = next;
} }
else { else {
NS_ASSERTION(line->mChildCount < 10000, "bad line count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line count");
// Make the line that just lost a frame dirty // Make the line that just lost a frame dirty
line->MarkDirty(); line->MarkDirty();
@ -4654,7 +4754,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
} }
void void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder) nsBlockReflowState::InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder)
{ {
// Set the geometric parent of the floater // Set the geometric parent of the floater
nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame(); nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
@ -4662,7 +4763,7 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// Then add the floater to the current line and place it when // Then add the floater to the current line and place it when
// appropriate // appropriate
AddFloater(aPlaceholder, PR_TRUE); AddFloater(aLineLayout, aPlaceholder, PR_TRUE);
} }
// This is called by the line layout's AddFloater method when a // This is called by the line layout's AddFloater method when a
@ -4671,7 +4772,8 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// then the floater is place immediately, otherwise the floater // then the floater is place immediately, otherwise the floater
// placement is deferred until the line has been reflowed. // placement is deferred until the line has been reflowed.
void void
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder, nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder,
PRBool aInitialReflow) PRBool aInitialReflow)
{ {
NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");
@ -4684,7 +4786,7 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Now place the floater immediately if possible. Otherwise stash it // Now place the floater immediately if possible. Otherwise stash it
// away in mPendingFloaters and place it later. // away in mPendingFloaters and place it later.
if (mLineLayout->CanPlaceFloaterNow()) { if (aLineLayout.CanPlaceFloaterNow()) {
nsRect combinedArea; nsRect combinedArea;
nsMargin floaterMargins; nsMargin floaterMargins;
nsMargin floaterOffsets; nsMargin floaterOffsets;
@ -4713,10 +4815,10 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Pass on updated available space to the current inline reflow engine // Pass on updated available space to the current inline reflow engine
GetAvailableSpace(); GetAvailableSpace();
mLineLayout->UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY,
mAvailSpaceRect.width, mAvailSpaceRect.width,
mAvailSpaceRect.height, mAvailSpaceRect.height,
isLeftFloater); isLeftFloater);
// Restore coordinate system // Restore coordinate system
mSpaceManager->Translate(dx, dy); mSpaceManager->Translate(dx, dy);
@ -5138,6 +5240,10 @@ nsBlockFrame::Paint(nsIPresContext& aPresContext,
const nsRect& aDirtyRect, const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer) nsFramePaintLayer aWhichLayer)
{ {
if (NS_FRAME_IS_UNFLOWABLE & mState) {
return NS_OK;
}
#ifdef NOISY_DAMAGE_REPAIR #ifdef NOISY_DAMAGE_REPAIR
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) { if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PRInt32 depth = GetDepth(); PRInt32 depth = GetDepth();
@ -5804,7 +5910,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
availSize.height = NS_UNCONSTRAINEDSIZE; availSize.height = NS_UNCONSTRAINEDSIZE;
nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState, nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState,
mBullet, availSize); mBullet, availSize);
reflowState.mLineLayout = aState.mLineLayout;
nsIHTMLReflow* htmlReflow; nsIHTMLReflow* htmlReflow;
nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
@ -5825,25 +5930,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
} }
void
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState)
{
// First reflow the bullet
nsLineLayout* lineLayout = aState.mLineLayout;
nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
if (mLines) {
// If we have at least one line then let line-layout position the
// bullet (this way the bullet is vertically aligned properly).
lineLayout->AddBulletFrame(mBullet, metrics);
}
else {
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
}
}
//XXX get rid of this -- its slow //XXX get rid of this -- its slow
void void
nsBlockFrame::BuildFloaterList() nsBlockFrame::BuildFloaterList()
@ -6054,7 +6140,7 @@ nsAnonymousBlockFrame::RemoveFirstFrame()
else { else {
// Remove frame from line and mark the line dirty // Remove frame from line and mark the line dirty
--line->mChildCount; --line->mChildCount;
NS_ASSERTION(line->mChildCount < 10000, "bad inline count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad inline count");
line->MarkDirty(); line->MarkDirty();
firstChild->GetNextSibling(&line->mFirstChild); firstChild->GetNextSibling(&line->mFirstChild);
} }
@ -6158,7 +6244,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
} }
} }
} }
NS_ASSERTION(line->mChildCount < 10000, "bad line child count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line child count");
count += line->mChildCount; count += line->mChildCount;
line = line->mNext; line = line->mNext;
} }

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIFocusTracker.h" #include "nsIFocusTracker.h"
#include "nsIFrameSelection.h" #include "nsIFrameSelection.h"
#define MAX_LINE_COUNT 50000
// XXX HTML:P's that are empty yet have style indicating they should // XXX HTML:P's that are empty yet have style indicating they should
// clear floaters - we need to ignore the clear behavior. // clear floaters - we need to ignore the clear behavior.
@ -203,8 +205,7 @@ public:
nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics);
nsLineLayout* aLineLayout);
~nsBlockReflowState(); ~nsBlockReflowState();
@ -240,9 +241,11 @@ public:
#endif #endif
} }
void InitFloater(nsPlaceholderFrame* aPlaceholderFrame); void InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame);
void AddFloater(nsPlaceholderFrame* aPlaceholderFrame, void AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame,
PRBool aInitialReflow); PRBool aInitialReflow);
PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats); PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats);
@ -302,13 +305,20 @@ public:
nscoord* aTopMarginResult, nscoord* aTopMarginResult,
nscoord* aBottomMarginResult); nscoord* aBottomMarginResult);
void ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult); void ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult);
void RecoverStateFrom(nsLineBox* aLine, void RecoverStateFrom(nsLineBox* aLine,
PRBool aApplyTopMargin, PRBool aApplyTopMargin,
nscoord aDeltaY, nscoord aDeltaY,
nsRect* aDamageRect); nsRect* aDamageRect);
void AdvanceToNextLine() {
mLineNumber++;
}
//---------------------------------------- //----------------------------------------
// This state is the "global" state computed once for the reflow of // This state is the "global" state computed once for the reflow of
@ -321,7 +331,7 @@ public:
const nsHTMLReflowState& mReflowState; const nsHTMLReflowState& mReflowState;
nsLineLayout* mLineLayout; // nsLineLayout* mLineLayout;
nsISpaceManager* mSpaceManager; nsISpaceManager* mSpaceManager;
@ -411,18 +421,22 @@ public:
PRBool mComputeMaxElementSize; PRBool mComputeMaxElementSize;
nsSize mMaxElementSize; nsSize mMaxElementSize;
nscoord mMinLineHeight;
PRInt32 mLineNumber;
}; };
// XXX This is vile. Make it go away // XXX This is vile. Make it go away
void void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame) nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->InitFloater(aFrame); mBlockRS->InitFloater(*this, aFrame);
} }
void void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->AddFloater(aFrame, PR_FALSE); mBlockRS->AddFloater(*this, aFrame, PR_FALSE);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -430,8 +444,7 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics)
nsLineLayout* aLineLayout)
: mBlock(aFrame), : mBlock(aFrame),
mPresContext(aPresContext), mPresContext(aPresContext),
mReflowState(aReflowState), mReflowState(aReflowState),
@ -439,10 +452,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
mIsBottomMarginRoot(PR_FALSE), mIsBottomMarginRoot(PR_FALSE),
mApplyTopMargin(PR_FALSE), mApplyTopMargin(PR_FALSE),
mNextRCFrame(nsnull), mNextRCFrame(nsnull),
mPrevBottomMargin(0) mPrevBottomMargin(0),
mLineNumber(0)
{ {
mLineLayout = aLineLayout;
mSpaceManager = aReflowState.mSpaceManager; mSpaceManager = aReflowState.mSpaceManager;
// Translate into our content area and then save the // Translate into our content area and then save the
@ -515,7 +527,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
break; break;
} }
mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;; mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;
mMaxElementSize.SizeTo(0, 0); mMaxElementSize.SizeTo(0, 0);
if (0 != borderPadding.top) { if (0 != borderPadding.top) {
@ -524,6 +536,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
if (0 != borderPadding.bottom) { if (0 != borderPadding.bottom) {
mIsBottomMarginRoot = PR_TRUE; mIsBottomMarginRoot = PR_TRUE;
} }
mMinLineHeight = nsHTMLReflowState::CalcLineHeight(*mPresContext,
aReflowState.frame);
} }
nsBlockReflowState::~nsBlockReflowState() nsBlockReflowState::~nsBlockReflowState()
@ -537,7 +552,9 @@ nsBlockReflowState::~nsBlockReflowState()
// at the current Y coordinate. This method assumes that // at the current Y coordinate. This method assumes that
// GetAvailableSpace has already been called. // GetAvailableSpace has already been called.
void void
nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType, nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult) nsRect& aResult)
{ {
nscoord availHeight = mUnconstrainedHeight nscoord availHeight = mUnconstrainedHeight
@ -546,7 +563,28 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
const nsMargin& borderPadding = BorderPadding(); const nsMargin& borderPadding = BorderPadding();
nscoord availX, availWidth; nscoord availX, availWidth;
if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
if (NS_STYLE_DISPLAY_LIST_ITEM == aDisplay->mDisplay) {
// XXX This is a hack until the css2 folks can figure how to deal
// with list-items and floaters.
nscoord leftMargin = 0;
#if 0
if (mAvailSpaceRect.x != borderPadding.left) {
// When a list-item is impacted by a left floater, slide it over
// by the right amount so that the bullets will (hopefully) be
// visible.
float p2t;
mPresContext->GetScaledPixelsToTwips(&p2t);
leftMargin = NSIntPixelsToTwips(40, p2t);
}
#endif
// Assume the frame is clueless about the space manager and only
// give it free space.
availX = mAvailSpaceRect.x + borderPadding.left + leftMargin;
availWidth = mAvailSpaceRect.width - leftMargin;
}
else if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
// Frames that know how to do non-rectangular splitting are given // Frames that know how to do non-rectangular splitting are given
// the entire available space, including space consumed by // the entire available space, including space consumed by
// floaters. // floaters.
@ -561,6 +599,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
availX = mAvailSpaceRect.x + borderPadding.left; availX = mAvailSpaceRect.x + borderPadding.left;
availWidth = mAvailSpaceRect.width; availWidth = mAvailSpaceRect.width;
} }
aResult.SetRect(availX, mY, availWidth, availHeight); aResult.SetRect(availX, mY, availWidth, availHeight);
} }
@ -630,10 +669,13 @@ nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine,
// Setup reflow state to compute the block childs top and bottom // Setup reflow state to compute the block childs top and bottom
// margins // margins
nsIFrame* frame = aLine->mFirstChild; nsIFrame* frame = aLine->mFirstChild;
nsRect availSpaceRect;
const nsStyleDisplay* display;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) display);
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpaceRect; ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect);
ComputeBlockAvailSpace(splitType, availSpaceRect);
nsSize availSpace(availSpaceRect.width, availSpaceRect.height); nsSize availSpace(availSpaceRect.width, availSpaceRect.height);
nsHTMLReflowState reflowState(*mPresContext, mReflowState, nsHTMLReflowState reflowState(*mPresContext, mReflowState,
frame, availSpace); frame, availSpace);
@ -1150,11 +1192,21 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState, const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus) nsReflowStatus& aStatus)
{ {
nsLineLayout lineLayout(aPresContext, aReflowState.mSpaceManager, if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
&aReflowState, nsnull != aMetrics.maxElementSize); #ifdef DEBUG_kipp
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics, {
&lineLayout); extern char* nsPresShell_ReflowStackPointerTop;
lineLayout.Init(&state); char marker;
char* newsp = (char*) &marker;
printf("XXX: frame tree is too deep; approx stack size = %d\n",
nsPresShell_ReflowStackPointerTop - newsp);
}
#endif
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics);
if (NS_BLOCK_MARGIN_ROOT & mFlags) { if (NS_BLOCK_MARGIN_ROOT & mFlags) {
state.mIsTopMarginRoot = PR_TRUE; state.mIsTopMarginRoot = PR_TRUE;
state.mIsBottomMarginRoot = PR_TRUE; state.mIsBottomMarginRoot = PR_TRUE;
@ -1887,9 +1939,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE; PRBool keepGoing = PR_TRUE;
// Inform line layout of where the text runs are
aState.mLineLayout->SetReflowTextRuns(mTextRuns);
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
if (aState.mReflowState.reason == eReflowReason_Incremental) { if (aState.mReflowState.reason == eReflowReason_Incremental) {
nsIReflowCommand::ReflowType type; nsIReflowCommand::ReflowType type;
@ -1980,7 +2029,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
// Pull data from a next-in-flow if we can // Pull data from a next-in-flow if we can
@ -2049,13 +2098,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
} }
// Handle an odd-ball case: a list-item with no lines // Handle an odd-ball case: a list-item with no lines
if (mBullet && HaveOutsideBullet() && !mLines) { if (mBullet && HaveOutsideBullet() && !mLines) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
} }
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
@ -2289,8 +2343,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock"); NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock");
NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters"); NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters");
} }
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (0 != --fromLine->mChildCount) { if (0 != --fromLine->mChildCount) {
NS_ASSERTION(fromLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(fromLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
// Mark line dirty now that we pulled a child // Mark line dirty now that we pulled a child
fromLine->MarkDirty(); fromLine->MarkDirty();
frame->GetNextSibling(&fromLine->mFirstChild); frame->GetNextSibling(&fromLine->mFirstChild);
@ -2585,6 +2640,38 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
return PR_FALSE; return PR_FALSE;
} }
#if 0
const nsStyleText* styleText;
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
if ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)) {
// Since whitespace is significant, we know that the paragraph
// is not empty (even if it has no text in it because it has
return PR_FALSE;
static PRBool
IsEmptyHTMLParagraph(nsIFrame* aFrame)
{
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf)) &&
nsBlockReflowContext::IsHTMLParagraph(aFrame)) {
if (!bf->mLines) {
// It's an html paragraph and it's empty
return PR_TRUE;
}
nsLineBox* line = bf->mLines;
while (line) {
if (!IsEmptyLine(line)) {
return PR_FALSE;
}
line = line->mNext;
}
}
return PR_FALSE;
}
#endif
nsIFrame* nsIFrame*
nsBlockFrame::GetTopBlockChild() nsBlockFrame::GetTopBlockChild()
{ {
@ -2650,52 +2737,6 @@ nsBlockFrame::GetTopBlockChild()
return nsnull; return nsnull;
} }
static void ComputeCombinedArea(const nsRect aRect1, const nsRect aRect2, nsRect& aOutRect)
{
// Rect 1's top left point: (aRect.x, aRect1.y)
// Rect 2's top left point: (aRect2.x, aRect2.y)
// Rect 2's bottom right point: (x2, y2)
// Output rect's top left point: (aOutRect.x, aOutRect.y)
// Output rect's bottom right point: (xOut, yOut)
//
// Calculate the top left point of the output rect
//
// Initialize output rect's top left point to Rect 1's top left point
aOutRect.x = aRect1.x;
aOutRect.y = aRect1.y;
if (aRect2.x < aRect1.x) {
aOutRect.x = aRect2.x;
}
if (aRect2.y < aRect1.y) {
aOutRect.y = aRect2.y;
}
//
// Calculate the bottom right point of the output rect
//
// Initialize output rect's bottom right point to Rect 1's bottom right point
nscoord xOut = aRect1.x + aRect1.width;
nscoord yOut = aRect1.y + aRect1.height;
// Initialize Rect 2's bottom right point
nscoord x2 = aRect2.x + aRect2.width;
nscoord y2 = aRect2.y + aRect2.height;
if (x2 > xOut) {
xOut = x2;
}
if (y2 > yOut) {
yOut = y2;
}
//
// Set the width and height on the output rect.
//
aOutRect.width = xOut - aOutRect.x;
aOutRect.height = yOut - aOutRect.y;
}
nsresult nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsLineBox* aLine, nsLineBox* aLine,
@ -2739,7 +2780,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpace; nsRect availSpace;
aState.ComputeBlockAvailSpace(splitType, availSpace); aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
// Reflow the block into the available space // Reflow the block into the available space
nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
@ -2802,7 +2843,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// Do not count the continuation child on the line it used // Do not count the continuation child on the line it used
// to be on // to be on
aLine->mChildCount--; aLine->mChildCount--;
NS_ASSERTION(aLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line count");
} }
// Advance to next line since some of the block fit. That way // Advance to next line since some of the block fit. That way
@ -2885,11 +2926,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
bbox.y = aState.BorderPadding().top + ascent - bbox.y = aState.BorderPadding().top + ascent -
metrics.ascent + topMargin; metrics.ascent + topMargin;
mBullet->SetRect(bbox); mBullet->SetRect(bbox);
// Fix for bug 8314. Include the bullet's area in the combined area
// of the current line.
ComputeCombinedArea((const nsRect) bbox, (const nsRect) aLine->mCombinedArea,
aLine->mCombinedArea);
} }
} }
else { else {
@ -2929,134 +2965,197 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
#ifdef DEBUG #ifdef DEBUG
PRInt32 spins = 0; PRInt32 spins = 0;
#endif #endif
for (;;) { PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
if (nsnull != aLine->mFloaters) { while (LINE_REFLOW_REDO == lineReflowStatus) {
// Forget all of the floaters on the line // Prevent overflowing limited thread stacks by creating
aLine->mFloaters->Clear(); // nsLineLayout from the heap when the frame tree depth gets
} // large.
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0); if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
aState.mPendingFloaters.Clear(); rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
&lineReflowStatus);
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
} }
else { else {
/* XXX get the height right! */ rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
availHeight = aState.mAvailSpaceRect.height; &lineReflowStatus);
} }
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount(); if (NS_FAILED(rv)) {
nsLineLayout* lineLayout = aState.mLineLayout; break;
lineLayout->BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == lineLayout->GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
lineLayout->SetFirstLetterStyleOK(PR_TRUE);
} }
// Reflow the frames that are already on the line first
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
lineLayout->EndLineReflow();
#ifdef DEBUG #ifdef DEBUG
spins++; spins++;
if (1000 == spins) { if (1000 == spins) {
ListTag(stdout); ListTag(stdout);
printf(": yikes! spinning on a line over 1000 times!\n"); printf(": yikes! spinning on a line over 1000 times!\n");
NS_ABORT(); NS_ABORT();
}
#endif
continue;
} }
#endif
}
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout* ll = new nsLineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
if (!ll) {
return NS_ERROR_OUT_OF_MEMORY;
}
ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
ll->SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
aLineReflowStatus);
ll->EndLineReflow();
delete ll;
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout lineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
lineLayout.SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
aKeepReflowGoing, aLineReflowStatus);
lineLayout.EndLineReflow();
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
if (nsnull != aLine->mFloaters) {
// Forget all of the floaters on the line
aLine->mFloaters->Clear();
}
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
aState.mPendingFloaters.Clear();
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
}
else {
/* XXX get the height right! */
availHeight = aState.mAvailSpaceRect.height;
}
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount();
aLineLayout.BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == aLineLayout.GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
}
// Reflow the frames that are already on the line first
nsresult rv = NS_OK;
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
}
else {
// If we are propogating out a break-before status then there is // If we are propogating out a break-before status then there is
// no point in placing the line. // no point in placing the line.
NS_ASSERTION(aLine->mChildCount < 10000, "bad line child count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
lineLayout->EndLineReflow(); rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing);
} }
else {
rv = PlaceLine(aState, aLine, aKeepReflowGoing);
}
break;
} }
*aLineReflowStatus = lineReflowStatus;
return rv; return rv;
} }
@ -3071,6 +3170,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
*/ */
nsresult nsresult
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus) PRUint8* aLineReflowStatus)
@ -3079,7 +3179,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// If it's currently ok to be reflowing in first-letter style then // If it's currently ok to be reflowing in first-letter style then
// we must be about to reflow a frame that has first-letter style. // we must be about to reflow a frame that has first-letter style.
PRBool reflowingFirstLetter = aState.mLineLayout->GetFirstLetterStyleOK(); PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
#ifdef NOISY_FIRST_LETTER #ifdef NOISY_FIRST_LETTER
ListTag(stdout); ListTag(stdout);
printf(": reflowing "); printf(": reflowing ");
@ -3088,9 +3188,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
#endif #endif
// Reflow the inline frame // Reflow the inline frame
nsLineLayout* lineLayout = aState.mLineLayout;
nsReflowStatus frameReflowStatus; nsReflowStatus frameReflowStatus;
nsresult rv = lineLayout->ReflowFrame(aFrame, &aState.mNextRCFrame, nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
frameReflowStatus); frameReflowStatus);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -3135,7 +3234,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
else { else {
// It's not the first child on this line so go ahead and split // It's not the first child on this line so go ahead and split
// the line. We will see the frame again on the next-line. // the line. We will see the frame again on the next-line.
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3157,7 +3256,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line, but after the frame just reflowed // Split line, but after the frame just reflowed
nsIFrame* nextFrame; nsIFrame* nextFrame;
aFrame->GetNextSibling(&nextFrame); aFrame->GetNextSibling(&nextFrame);
rv = SplitLine(aState, aLine, nextFrame); rv = SplitLine(aState, aLineLayout, aLine, nextFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3203,7 +3302,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line after the current frame // Split line after the current frame
*aLineReflowStatus = LINE_REFLOW_STOP; *aLineReflowStatus = LINE_REFLOW_STOP;
aFrame->GetNextSibling(&aFrame); aFrame->GetNextSibling(&aFrame);
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3241,6 +3340,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
if (nsnull != nextInFlow) { if (nsnull != nextInFlow) {
aMadeNewFrame = PR_TRUE; aMadeNewFrame = PR_TRUE;
aLine->mChildCount++; aLine->mChildCount++;
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_FALSE); VerifyLines(PR_FALSE);
@ -3250,11 +3350,11 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
nsresult nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsBlockFrame::SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame) nsIFrame* aFrame)
{ {
nsLineLayout* lineLayout = aState.mLineLayout; PRInt32 pushCount = aLine->ChildCount() - aLineLayout.GetCurrentSpanCount();
PRInt32 pushCount = aLine->ChildCount() - lineLayout->GetCurrentSpanCount();
NS_ASSERTION(pushCount >= 0, "bad push count"); NS_ASSERTION(pushCount >= 0, "bad push count");
//printf("BEFORE (pushCount=%d):\n", pushCount); //printf("BEFORE (pushCount=%d):\n", pushCount);
//aLine->List(stdout, 0); //aLine->List(stdout, 0);
@ -3294,7 +3394,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState,
// Let line layout know that some frames are no longer part of its // Let line layout know that some frames are no longer part of its
// state. // state.
if (!aLine->IsBlock()) { if (!aLine->IsBlock()) {
lineLayout->SplitLineTo(aLine->ChildCount()); aLineLayout.SplitLineTo(aLine->ChildCount());
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_TRUE); VerifyLines(PR_TRUE);
@ -3340,6 +3440,7 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
nsresult nsresult
nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing) PRBool* aKeepReflowGoing)
{ {
@ -3360,15 +3461,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// method is used for placing a line of inline frames. If the rare // method is used for placing a line of inline frames. If the rare
// case is happening then the worst that will happen is that the // case is happening then the worst that will happen is that the
// bullet frame will be reflowed twice. // bullet frame will be reflowed twice.
nsLineLayout* lineLayout = aState.mLineLayout;
PRBool addedBullet = PR_FALSE; PRBool addedBullet = PR_FALSE;
if (HaveOutsideBullet() && (aLine == mLines) && if (HaveOutsideBullet() && (aLine == mLines) &&
(!lineLayout->IsZeroHeight() || !aLine->mNext)) { (!aLineLayout.IsZeroHeight() || !aLine->mNext)) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
aLineLayout.AddBulletFrame(mBullet, metrics);
addedBullet = PR_TRUE; addedBullet = PR_TRUE;
} }
nsSize maxElementSize; nsSize maxElementSize;
lineLayout->VerticalAlignFrames(aLine->mBounds, maxElementSize); aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize);
#ifdef DEBUG #ifdef DEBUG
{ {
static nscoord lastHeight = 0; static nscoord lastHeight = 0;
@ -3397,11 +3499,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
#else #else
PRBool allowJustify = PR_FALSE; PRBool allowJustify = PR_FALSE;
#endif #endif
lineLayout->TrimTrailingWhiteSpace(aLine->mBounds); aLineLayout.TrimTrailingWhiteSpace(aLine->mBounds);
lineLayout->HorizontalAlignFrames(aLine->mBounds, allowJustify); aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
lineLayout->RelativePositionFrames(aLine->mCombinedArea); aLineLayout.RelativePositionFrames(aLine->mCombinedArea);
if (addedBullet) { if (addedBullet) {
lineLayout->RemoveBulletFrame(mBullet); aLineLayout.RemoveBulletFrame(mBullet);
} }
// Inline lines do not have margins themselves; however they are // Inline lines do not have margins themselves; however they are
@ -3443,7 +3545,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
*aKeepReflowGoing = PR_FALSE; *aKeepReflowGoing = PR_FALSE;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -3491,8 +3592,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
break; break;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -4052,6 +4151,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
else { else {
prevSibLine->mChildCount++; prevSibLine->mChildCount++;
prevSibLine->MarkDirty(); prevSibLine->MarkDirty();
NS_ASSERTION(prevSibLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
aPrevSibling = newFrame; aPrevSibling = newFrame;
@ -4400,7 +4500,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
line = next; line = next;
} }
else { else {
NS_ASSERTION(line->mChildCount < 10000, "bad line count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line count");
// Make the line that just lost a frame dirty // Make the line that just lost a frame dirty
line->MarkDirty(); line->MarkDirty();
@ -4654,7 +4754,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
} }
void void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder) nsBlockReflowState::InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder)
{ {
// Set the geometric parent of the floater // Set the geometric parent of the floater
nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame(); nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
@ -4662,7 +4763,7 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// Then add the floater to the current line and place it when // Then add the floater to the current line and place it when
// appropriate // appropriate
AddFloater(aPlaceholder, PR_TRUE); AddFloater(aLineLayout, aPlaceholder, PR_TRUE);
} }
// This is called by the line layout's AddFloater method when a // This is called by the line layout's AddFloater method when a
@ -4671,7 +4772,8 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// then the floater is place immediately, otherwise the floater // then the floater is place immediately, otherwise the floater
// placement is deferred until the line has been reflowed. // placement is deferred until the line has been reflowed.
void void
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder, nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder,
PRBool aInitialReflow) PRBool aInitialReflow)
{ {
NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");
@ -4684,7 +4786,7 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Now place the floater immediately if possible. Otherwise stash it // Now place the floater immediately if possible. Otherwise stash it
// away in mPendingFloaters and place it later. // away in mPendingFloaters and place it later.
if (mLineLayout->CanPlaceFloaterNow()) { if (aLineLayout.CanPlaceFloaterNow()) {
nsRect combinedArea; nsRect combinedArea;
nsMargin floaterMargins; nsMargin floaterMargins;
nsMargin floaterOffsets; nsMargin floaterOffsets;
@ -4713,10 +4815,10 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Pass on updated available space to the current inline reflow engine // Pass on updated available space to the current inline reflow engine
GetAvailableSpace(); GetAvailableSpace();
mLineLayout->UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY,
mAvailSpaceRect.width, mAvailSpaceRect.width,
mAvailSpaceRect.height, mAvailSpaceRect.height,
isLeftFloater); isLeftFloater);
// Restore coordinate system // Restore coordinate system
mSpaceManager->Translate(dx, dy); mSpaceManager->Translate(dx, dy);
@ -5138,6 +5240,10 @@ nsBlockFrame::Paint(nsIPresContext& aPresContext,
const nsRect& aDirtyRect, const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer) nsFramePaintLayer aWhichLayer)
{ {
if (NS_FRAME_IS_UNFLOWABLE & mState) {
return NS_OK;
}
#ifdef NOISY_DAMAGE_REPAIR #ifdef NOISY_DAMAGE_REPAIR
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) { if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PRInt32 depth = GetDepth(); PRInt32 depth = GetDepth();
@ -5804,7 +5910,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
availSize.height = NS_UNCONSTRAINEDSIZE; availSize.height = NS_UNCONSTRAINEDSIZE;
nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState, nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState,
mBullet, availSize); mBullet, availSize);
reflowState.mLineLayout = aState.mLineLayout;
nsIHTMLReflow* htmlReflow; nsIHTMLReflow* htmlReflow;
nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
@ -5825,25 +5930,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
} }
void
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState)
{
// First reflow the bullet
nsLineLayout* lineLayout = aState.mLineLayout;
nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
if (mLines) {
// If we have at least one line then let line-layout position the
// bullet (this way the bullet is vertically aligned properly).
lineLayout->AddBulletFrame(mBullet, metrics);
}
else {
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
}
}
//XXX get rid of this -- its slow //XXX get rid of this -- its slow
void void
nsBlockFrame::BuildFloaterList() nsBlockFrame::BuildFloaterList()
@ -6054,7 +6140,7 @@ nsAnonymousBlockFrame::RemoveFirstFrame()
else { else {
// Remove frame from line and mark the line dirty // Remove frame from line and mark the line dirty
--line->mChildCount; --line->mChildCount;
NS_ASSERTION(line->mChildCount < 10000, "bad inline count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad inline count");
line->MarkDirty(); line->MarkDirty();
firstChild->GetNextSibling(&line->mFirstChild); firstChild->GetNextSibling(&line->mFirstChild);
} }
@ -6158,7 +6244,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
} }
} }
} }
NS_ASSERTION(line->mChildCount < 10000, "bad line child count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line child count");
count += line->mChildCount; count += line->mChildCount;
line = line->mNext; line = line->mNext;
} }

Просмотреть файл

@ -225,6 +225,7 @@ protected:
PRBool aKeepReflowGoing); PRBool aKeepReflowGoing);
nsresult PlaceLine(nsBlockReflowState& aState, nsresult PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing); PRBool* aKeepReflowGoing);
@ -258,7 +259,24 @@ protected:
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepLineGoing); PRBool* aKeepLineGoing);
nsresult DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus);
nsresult DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus);
nsresult DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus);
nsresult ReflowInlineFrame(nsBlockReflowState& aState, nsresult ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus); PRUint8* aLineReflowStatus);
@ -278,6 +296,7 @@ protected:
PRBool& aMadeNewFrame); PRBool& aMadeNewFrame);
nsresult SplitLine(nsBlockReflowState& aState, nsresult SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame); nsIFrame* aFrame);
@ -332,8 +351,6 @@ protected:
void ReflowBullet(nsBlockReflowState& aState, void ReflowBullet(nsBlockReflowState& aState,
nsHTMLReflowMetrics& aMetrics); nsHTMLReflowMetrics& aMetrics);
void PlaceBullet(nsBlockReflowState& aState);
//---------------------------------------- //----------------------------------------
nsIFrame* LastChild(); nsIFrame* LastChild();

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIFocusTracker.h" #include "nsIFocusTracker.h"
#include "nsIFrameSelection.h" #include "nsIFrameSelection.h"
#define MAX_LINE_COUNT 50000
// XXX HTML:P's that are empty yet have style indicating they should // XXX HTML:P's that are empty yet have style indicating they should
// clear floaters - we need to ignore the clear behavior. // clear floaters - we need to ignore the clear behavior.
@ -203,8 +205,7 @@ public:
nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics);
nsLineLayout* aLineLayout);
~nsBlockReflowState(); ~nsBlockReflowState();
@ -240,9 +241,11 @@ public:
#endif #endif
} }
void InitFloater(nsPlaceholderFrame* aPlaceholderFrame); void InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame);
void AddFloater(nsPlaceholderFrame* aPlaceholderFrame, void AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame,
PRBool aInitialReflow); PRBool aInitialReflow);
PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats); PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats);
@ -302,13 +305,20 @@ public:
nscoord* aTopMarginResult, nscoord* aTopMarginResult,
nscoord* aBottomMarginResult); nscoord* aBottomMarginResult);
void ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult); void ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult);
void RecoverStateFrom(nsLineBox* aLine, void RecoverStateFrom(nsLineBox* aLine,
PRBool aApplyTopMargin, PRBool aApplyTopMargin,
nscoord aDeltaY, nscoord aDeltaY,
nsRect* aDamageRect); nsRect* aDamageRect);
void AdvanceToNextLine() {
mLineNumber++;
}
//---------------------------------------- //----------------------------------------
// This state is the "global" state computed once for the reflow of // This state is the "global" state computed once for the reflow of
@ -321,7 +331,7 @@ public:
const nsHTMLReflowState& mReflowState; const nsHTMLReflowState& mReflowState;
nsLineLayout* mLineLayout; // nsLineLayout* mLineLayout;
nsISpaceManager* mSpaceManager; nsISpaceManager* mSpaceManager;
@ -411,18 +421,22 @@ public:
PRBool mComputeMaxElementSize; PRBool mComputeMaxElementSize;
nsSize mMaxElementSize; nsSize mMaxElementSize;
nscoord mMinLineHeight;
PRInt32 mLineNumber;
}; };
// XXX This is vile. Make it go away // XXX This is vile. Make it go away
void void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame) nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->InitFloater(aFrame); mBlockRS->InitFloater(*this, aFrame);
} }
void void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->AddFloater(aFrame, PR_FALSE); mBlockRS->AddFloater(*this, aFrame, PR_FALSE);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -430,8 +444,7 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics)
nsLineLayout* aLineLayout)
: mBlock(aFrame), : mBlock(aFrame),
mPresContext(aPresContext), mPresContext(aPresContext),
mReflowState(aReflowState), mReflowState(aReflowState),
@ -439,10 +452,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
mIsBottomMarginRoot(PR_FALSE), mIsBottomMarginRoot(PR_FALSE),
mApplyTopMargin(PR_FALSE), mApplyTopMargin(PR_FALSE),
mNextRCFrame(nsnull), mNextRCFrame(nsnull),
mPrevBottomMargin(0) mPrevBottomMargin(0),
mLineNumber(0)
{ {
mLineLayout = aLineLayout;
mSpaceManager = aReflowState.mSpaceManager; mSpaceManager = aReflowState.mSpaceManager;
// Translate into our content area and then save the // Translate into our content area and then save the
@ -515,7 +527,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
break; break;
} }
mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;; mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;
mMaxElementSize.SizeTo(0, 0); mMaxElementSize.SizeTo(0, 0);
if (0 != borderPadding.top) { if (0 != borderPadding.top) {
@ -524,6 +536,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
if (0 != borderPadding.bottom) { if (0 != borderPadding.bottom) {
mIsBottomMarginRoot = PR_TRUE; mIsBottomMarginRoot = PR_TRUE;
} }
mMinLineHeight = nsHTMLReflowState::CalcLineHeight(*mPresContext,
aReflowState.frame);
} }
nsBlockReflowState::~nsBlockReflowState() nsBlockReflowState::~nsBlockReflowState()
@ -537,7 +552,9 @@ nsBlockReflowState::~nsBlockReflowState()
// at the current Y coordinate. This method assumes that // at the current Y coordinate. This method assumes that
// GetAvailableSpace has already been called. // GetAvailableSpace has already been called.
void void
nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType, nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult) nsRect& aResult)
{ {
nscoord availHeight = mUnconstrainedHeight nscoord availHeight = mUnconstrainedHeight
@ -546,7 +563,28 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
const nsMargin& borderPadding = BorderPadding(); const nsMargin& borderPadding = BorderPadding();
nscoord availX, availWidth; nscoord availX, availWidth;
if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
if (NS_STYLE_DISPLAY_LIST_ITEM == aDisplay->mDisplay) {
// XXX This is a hack until the css2 folks can figure how to deal
// with list-items and floaters.
nscoord leftMargin = 0;
#if 0
if (mAvailSpaceRect.x != borderPadding.left) {
// When a list-item is impacted by a left floater, slide it over
// by the right amount so that the bullets will (hopefully) be
// visible.
float p2t;
mPresContext->GetScaledPixelsToTwips(&p2t);
leftMargin = NSIntPixelsToTwips(40, p2t);
}
#endif
// Assume the frame is clueless about the space manager and only
// give it free space.
availX = mAvailSpaceRect.x + borderPadding.left + leftMargin;
availWidth = mAvailSpaceRect.width - leftMargin;
}
else if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
// Frames that know how to do non-rectangular splitting are given // Frames that know how to do non-rectangular splitting are given
// the entire available space, including space consumed by // the entire available space, including space consumed by
// floaters. // floaters.
@ -561,6 +599,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
availX = mAvailSpaceRect.x + borderPadding.left; availX = mAvailSpaceRect.x + borderPadding.left;
availWidth = mAvailSpaceRect.width; availWidth = mAvailSpaceRect.width;
} }
aResult.SetRect(availX, mY, availWidth, availHeight); aResult.SetRect(availX, mY, availWidth, availHeight);
} }
@ -630,10 +669,13 @@ nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine,
// Setup reflow state to compute the block childs top and bottom // Setup reflow state to compute the block childs top and bottom
// margins // margins
nsIFrame* frame = aLine->mFirstChild; nsIFrame* frame = aLine->mFirstChild;
nsRect availSpaceRect;
const nsStyleDisplay* display;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) display);
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpaceRect; ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect);
ComputeBlockAvailSpace(splitType, availSpaceRect);
nsSize availSpace(availSpaceRect.width, availSpaceRect.height); nsSize availSpace(availSpaceRect.width, availSpaceRect.height);
nsHTMLReflowState reflowState(*mPresContext, mReflowState, nsHTMLReflowState reflowState(*mPresContext, mReflowState,
frame, availSpace); frame, availSpace);
@ -1150,11 +1192,21 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState, const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus) nsReflowStatus& aStatus)
{ {
nsLineLayout lineLayout(aPresContext, aReflowState.mSpaceManager, if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
&aReflowState, nsnull != aMetrics.maxElementSize); #ifdef DEBUG_kipp
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics, {
&lineLayout); extern char* nsPresShell_ReflowStackPointerTop;
lineLayout.Init(&state); char marker;
char* newsp = (char*) &marker;
printf("XXX: frame tree is too deep; approx stack size = %d\n",
nsPresShell_ReflowStackPointerTop - newsp);
}
#endif
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics);
if (NS_BLOCK_MARGIN_ROOT & mFlags) { if (NS_BLOCK_MARGIN_ROOT & mFlags) {
state.mIsTopMarginRoot = PR_TRUE; state.mIsTopMarginRoot = PR_TRUE;
state.mIsBottomMarginRoot = PR_TRUE; state.mIsBottomMarginRoot = PR_TRUE;
@ -1887,9 +1939,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE; PRBool keepGoing = PR_TRUE;
// Inform line layout of where the text runs are
aState.mLineLayout->SetReflowTextRuns(mTextRuns);
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
if (aState.mReflowState.reason == eReflowReason_Incremental) { if (aState.mReflowState.reason == eReflowReason_Incremental) {
nsIReflowCommand::ReflowType type; nsIReflowCommand::ReflowType type;
@ -1980,7 +2029,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
// Pull data from a next-in-flow if we can // Pull data from a next-in-flow if we can
@ -2049,13 +2098,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
} }
// Handle an odd-ball case: a list-item with no lines // Handle an odd-ball case: a list-item with no lines
if (mBullet && HaveOutsideBullet() && !mLines) { if (mBullet && HaveOutsideBullet() && !mLines) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
} }
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
@ -2289,8 +2343,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock"); NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock");
NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters"); NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters");
} }
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (0 != --fromLine->mChildCount) { if (0 != --fromLine->mChildCount) {
NS_ASSERTION(fromLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(fromLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
// Mark line dirty now that we pulled a child // Mark line dirty now that we pulled a child
fromLine->MarkDirty(); fromLine->MarkDirty();
frame->GetNextSibling(&fromLine->mFirstChild); frame->GetNextSibling(&fromLine->mFirstChild);
@ -2585,6 +2640,38 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
return PR_FALSE; return PR_FALSE;
} }
#if 0
const nsStyleText* styleText;
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
if ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)) {
// Since whitespace is significant, we know that the paragraph
// is not empty (even if it has no text in it because it has
return PR_FALSE;
static PRBool
IsEmptyHTMLParagraph(nsIFrame* aFrame)
{
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf)) &&
nsBlockReflowContext::IsHTMLParagraph(aFrame)) {
if (!bf->mLines) {
// It's an html paragraph and it's empty
return PR_TRUE;
}
nsLineBox* line = bf->mLines;
while (line) {
if (!IsEmptyLine(line)) {
return PR_FALSE;
}
line = line->mNext;
}
}
return PR_FALSE;
}
#endif
nsIFrame* nsIFrame*
nsBlockFrame::GetTopBlockChild() nsBlockFrame::GetTopBlockChild()
{ {
@ -2650,52 +2737,6 @@ nsBlockFrame::GetTopBlockChild()
return nsnull; return nsnull;
} }
static void ComputeCombinedArea(const nsRect aRect1, const nsRect aRect2, nsRect& aOutRect)
{
// Rect 1's top left point: (aRect.x, aRect1.y)
// Rect 2's top left point: (aRect2.x, aRect2.y)
// Rect 2's bottom right point: (x2, y2)
// Output rect's top left point: (aOutRect.x, aOutRect.y)
// Output rect's bottom right point: (xOut, yOut)
//
// Calculate the top left point of the output rect
//
// Initialize output rect's top left point to Rect 1's top left point
aOutRect.x = aRect1.x;
aOutRect.y = aRect1.y;
if (aRect2.x < aRect1.x) {
aOutRect.x = aRect2.x;
}
if (aRect2.y < aRect1.y) {
aOutRect.y = aRect2.y;
}
//
// Calculate the bottom right point of the output rect
//
// Initialize output rect's bottom right point to Rect 1's bottom right point
nscoord xOut = aRect1.x + aRect1.width;
nscoord yOut = aRect1.y + aRect1.height;
// Initialize Rect 2's bottom right point
nscoord x2 = aRect2.x + aRect2.width;
nscoord y2 = aRect2.y + aRect2.height;
if (x2 > xOut) {
xOut = x2;
}
if (y2 > yOut) {
yOut = y2;
}
//
// Set the width and height on the output rect.
//
aOutRect.width = xOut - aOutRect.x;
aOutRect.height = yOut - aOutRect.y;
}
nsresult nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsLineBox* aLine, nsLineBox* aLine,
@ -2739,7 +2780,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpace; nsRect availSpace;
aState.ComputeBlockAvailSpace(splitType, availSpace); aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
// Reflow the block into the available space // Reflow the block into the available space
nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
@ -2802,7 +2843,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// Do not count the continuation child on the line it used // Do not count the continuation child on the line it used
// to be on // to be on
aLine->mChildCount--; aLine->mChildCount--;
NS_ASSERTION(aLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line count");
} }
// Advance to next line since some of the block fit. That way // Advance to next line since some of the block fit. That way
@ -2885,11 +2926,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
bbox.y = aState.BorderPadding().top + ascent - bbox.y = aState.BorderPadding().top + ascent -
metrics.ascent + topMargin; metrics.ascent + topMargin;
mBullet->SetRect(bbox); mBullet->SetRect(bbox);
// Fix for bug 8314. Include the bullet's area in the combined area
// of the current line.
ComputeCombinedArea((const nsRect) bbox, (const nsRect) aLine->mCombinedArea,
aLine->mCombinedArea);
} }
} }
else { else {
@ -2929,134 +2965,197 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
#ifdef DEBUG #ifdef DEBUG
PRInt32 spins = 0; PRInt32 spins = 0;
#endif #endif
for (;;) { PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
if (nsnull != aLine->mFloaters) { while (LINE_REFLOW_REDO == lineReflowStatus) {
// Forget all of the floaters on the line // Prevent overflowing limited thread stacks by creating
aLine->mFloaters->Clear(); // nsLineLayout from the heap when the frame tree depth gets
} // large.
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0); if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
aState.mPendingFloaters.Clear(); rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
&lineReflowStatus);
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
} }
else { else {
/* XXX get the height right! */ rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
availHeight = aState.mAvailSpaceRect.height; &lineReflowStatus);
} }
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount(); if (NS_FAILED(rv)) {
nsLineLayout* lineLayout = aState.mLineLayout; break;
lineLayout->BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == lineLayout->GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
lineLayout->SetFirstLetterStyleOK(PR_TRUE);
} }
// Reflow the frames that are already on the line first
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
lineLayout->EndLineReflow();
#ifdef DEBUG #ifdef DEBUG
spins++; spins++;
if (1000 == spins) { if (1000 == spins) {
ListTag(stdout); ListTag(stdout);
printf(": yikes! spinning on a line over 1000 times!\n"); printf(": yikes! spinning on a line over 1000 times!\n");
NS_ABORT(); NS_ABORT();
}
#endif
continue;
} }
#endif
}
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout* ll = new nsLineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
if (!ll) {
return NS_ERROR_OUT_OF_MEMORY;
}
ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
ll->SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
aLineReflowStatus);
ll->EndLineReflow();
delete ll;
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout lineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
lineLayout.SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
aKeepReflowGoing, aLineReflowStatus);
lineLayout.EndLineReflow();
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
if (nsnull != aLine->mFloaters) {
// Forget all of the floaters on the line
aLine->mFloaters->Clear();
}
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
aState.mPendingFloaters.Clear();
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
}
else {
/* XXX get the height right! */
availHeight = aState.mAvailSpaceRect.height;
}
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount();
aLineLayout.BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == aLineLayout.GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
}
// Reflow the frames that are already on the line first
nsresult rv = NS_OK;
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
}
else {
// If we are propogating out a break-before status then there is // If we are propogating out a break-before status then there is
// no point in placing the line. // no point in placing the line.
NS_ASSERTION(aLine->mChildCount < 10000, "bad line child count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
lineLayout->EndLineReflow(); rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing);
} }
else {
rv = PlaceLine(aState, aLine, aKeepReflowGoing);
}
break;
} }
*aLineReflowStatus = lineReflowStatus;
return rv; return rv;
} }
@ -3071,6 +3170,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
*/ */
nsresult nsresult
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus) PRUint8* aLineReflowStatus)
@ -3079,7 +3179,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// If it's currently ok to be reflowing in first-letter style then // If it's currently ok to be reflowing in first-letter style then
// we must be about to reflow a frame that has first-letter style. // we must be about to reflow a frame that has first-letter style.
PRBool reflowingFirstLetter = aState.mLineLayout->GetFirstLetterStyleOK(); PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
#ifdef NOISY_FIRST_LETTER #ifdef NOISY_FIRST_LETTER
ListTag(stdout); ListTag(stdout);
printf(": reflowing "); printf(": reflowing ");
@ -3088,9 +3188,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
#endif #endif
// Reflow the inline frame // Reflow the inline frame
nsLineLayout* lineLayout = aState.mLineLayout;
nsReflowStatus frameReflowStatus; nsReflowStatus frameReflowStatus;
nsresult rv = lineLayout->ReflowFrame(aFrame, &aState.mNextRCFrame, nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
frameReflowStatus); frameReflowStatus);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -3135,7 +3234,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
else { else {
// It's not the first child on this line so go ahead and split // It's not the first child on this line so go ahead and split
// the line. We will see the frame again on the next-line. // the line. We will see the frame again on the next-line.
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3157,7 +3256,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line, but after the frame just reflowed // Split line, but after the frame just reflowed
nsIFrame* nextFrame; nsIFrame* nextFrame;
aFrame->GetNextSibling(&nextFrame); aFrame->GetNextSibling(&nextFrame);
rv = SplitLine(aState, aLine, nextFrame); rv = SplitLine(aState, aLineLayout, aLine, nextFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3203,7 +3302,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line after the current frame // Split line after the current frame
*aLineReflowStatus = LINE_REFLOW_STOP; *aLineReflowStatus = LINE_REFLOW_STOP;
aFrame->GetNextSibling(&aFrame); aFrame->GetNextSibling(&aFrame);
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3241,6 +3340,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
if (nsnull != nextInFlow) { if (nsnull != nextInFlow) {
aMadeNewFrame = PR_TRUE; aMadeNewFrame = PR_TRUE;
aLine->mChildCount++; aLine->mChildCount++;
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_FALSE); VerifyLines(PR_FALSE);
@ -3250,11 +3350,11 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
nsresult nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsBlockFrame::SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame) nsIFrame* aFrame)
{ {
nsLineLayout* lineLayout = aState.mLineLayout; PRInt32 pushCount = aLine->ChildCount() - aLineLayout.GetCurrentSpanCount();
PRInt32 pushCount = aLine->ChildCount() - lineLayout->GetCurrentSpanCount();
NS_ASSERTION(pushCount >= 0, "bad push count"); NS_ASSERTION(pushCount >= 0, "bad push count");
//printf("BEFORE (pushCount=%d):\n", pushCount); //printf("BEFORE (pushCount=%d):\n", pushCount);
//aLine->List(stdout, 0); //aLine->List(stdout, 0);
@ -3294,7 +3394,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState,
// Let line layout know that some frames are no longer part of its // Let line layout know that some frames are no longer part of its
// state. // state.
if (!aLine->IsBlock()) { if (!aLine->IsBlock()) {
lineLayout->SplitLineTo(aLine->ChildCount()); aLineLayout.SplitLineTo(aLine->ChildCount());
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_TRUE); VerifyLines(PR_TRUE);
@ -3340,6 +3440,7 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
nsresult nsresult
nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing) PRBool* aKeepReflowGoing)
{ {
@ -3360,15 +3461,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// method is used for placing a line of inline frames. If the rare // method is used for placing a line of inline frames. If the rare
// case is happening then the worst that will happen is that the // case is happening then the worst that will happen is that the
// bullet frame will be reflowed twice. // bullet frame will be reflowed twice.
nsLineLayout* lineLayout = aState.mLineLayout;
PRBool addedBullet = PR_FALSE; PRBool addedBullet = PR_FALSE;
if (HaveOutsideBullet() && (aLine == mLines) && if (HaveOutsideBullet() && (aLine == mLines) &&
(!lineLayout->IsZeroHeight() || !aLine->mNext)) { (!aLineLayout.IsZeroHeight() || !aLine->mNext)) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
aLineLayout.AddBulletFrame(mBullet, metrics);
addedBullet = PR_TRUE; addedBullet = PR_TRUE;
} }
nsSize maxElementSize; nsSize maxElementSize;
lineLayout->VerticalAlignFrames(aLine->mBounds, maxElementSize); aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize);
#ifdef DEBUG #ifdef DEBUG
{ {
static nscoord lastHeight = 0; static nscoord lastHeight = 0;
@ -3397,11 +3499,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
#else #else
PRBool allowJustify = PR_FALSE; PRBool allowJustify = PR_FALSE;
#endif #endif
lineLayout->TrimTrailingWhiteSpace(aLine->mBounds); aLineLayout.TrimTrailingWhiteSpace(aLine->mBounds);
lineLayout->HorizontalAlignFrames(aLine->mBounds, allowJustify); aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
lineLayout->RelativePositionFrames(aLine->mCombinedArea); aLineLayout.RelativePositionFrames(aLine->mCombinedArea);
if (addedBullet) { if (addedBullet) {
lineLayout->RemoveBulletFrame(mBullet); aLineLayout.RemoveBulletFrame(mBullet);
} }
// Inline lines do not have margins themselves; however they are // Inline lines do not have margins themselves; however they are
@ -3443,7 +3545,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
*aKeepReflowGoing = PR_FALSE; *aKeepReflowGoing = PR_FALSE;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -3491,8 +3592,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
break; break;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -4052,6 +4151,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
else { else {
prevSibLine->mChildCount++; prevSibLine->mChildCount++;
prevSibLine->MarkDirty(); prevSibLine->MarkDirty();
NS_ASSERTION(prevSibLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
aPrevSibling = newFrame; aPrevSibling = newFrame;
@ -4400,7 +4500,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
line = next; line = next;
} }
else { else {
NS_ASSERTION(line->mChildCount < 10000, "bad line count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line count");
// Make the line that just lost a frame dirty // Make the line that just lost a frame dirty
line->MarkDirty(); line->MarkDirty();
@ -4654,7 +4754,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
} }
void void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder) nsBlockReflowState::InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder)
{ {
// Set the geometric parent of the floater // Set the geometric parent of the floater
nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame(); nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
@ -4662,7 +4763,7 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// Then add the floater to the current line and place it when // Then add the floater to the current line and place it when
// appropriate // appropriate
AddFloater(aPlaceholder, PR_TRUE); AddFloater(aLineLayout, aPlaceholder, PR_TRUE);
} }
// This is called by the line layout's AddFloater method when a // This is called by the line layout's AddFloater method when a
@ -4671,7 +4772,8 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// then the floater is place immediately, otherwise the floater // then the floater is place immediately, otherwise the floater
// placement is deferred until the line has been reflowed. // placement is deferred until the line has been reflowed.
void void
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder, nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder,
PRBool aInitialReflow) PRBool aInitialReflow)
{ {
NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");
@ -4684,7 +4786,7 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Now place the floater immediately if possible. Otherwise stash it // Now place the floater immediately if possible. Otherwise stash it
// away in mPendingFloaters and place it later. // away in mPendingFloaters and place it later.
if (mLineLayout->CanPlaceFloaterNow()) { if (aLineLayout.CanPlaceFloaterNow()) {
nsRect combinedArea; nsRect combinedArea;
nsMargin floaterMargins; nsMargin floaterMargins;
nsMargin floaterOffsets; nsMargin floaterOffsets;
@ -4713,10 +4815,10 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Pass on updated available space to the current inline reflow engine // Pass on updated available space to the current inline reflow engine
GetAvailableSpace(); GetAvailableSpace();
mLineLayout->UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY,
mAvailSpaceRect.width, mAvailSpaceRect.width,
mAvailSpaceRect.height, mAvailSpaceRect.height,
isLeftFloater); isLeftFloater);
// Restore coordinate system // Restore coordinate system
mSpaceManager->Translate(dx, dy); mSpaceManager->Translate(dx, dy);
@ -5138,6 +5240,10 @@ nsBlockFrame::Paint(nsIPresContext& aPresContext,
const nsRect& aDirtyRect, const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer) nsFramePaintLayer aWhichLayer)
{ {
if (NS_FRAME_IS_UNFLOWABLE & mState) {
return NS_OK;
}
#ifdef NOISY_DAMAGE_REPAIR #ifdef NOISY_DAMAGE_REPAIR
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) { if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PRInt32 depth = GetDepth(); PRInt32 depth = GetDepth();
@ -5804,7 +5910,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
availSize.height = NS_UNCONSTRAINEDSIZE; availSize.height = NS_UNCONSTRAINEDSIZE;
nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState, nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState,
mBullet, availSize); mBullet, availSize);
reflowState.mLineLayout = aState.mLineLayout;
nsIHTMLReflow* htmlReflow; nsIHTMLReflow* htmlReflow;
nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
@ -5825,25 +5930,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
} }
void
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState)
{
// First reflow the bullet
nsLineLayout* lineLayout = aState.mLineLayout;
nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
if (mLines) {
// If we have at least one line then let line-layout position the
// bullet (this way the bullet is vertically aligned properly).
lineLayout->AddBulletFrame(mBullet, metrics);
}
else {
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
}
}
//XXX get rid of this -- its slow //XXX get rid of this -- its slow
void void
nsBlockFrame::BuildFloaterList() nsBlockFrame::BuildFloaterList()
@ -6054,7 +6140,7 @@ nsAnonymousBlockFrame::RemoveFirstFrame()
else { else {
// Remove frame from line and mark the line dirty // Remove frame from line and mark the line dirty
--line->mChildCount; --line->mChildCount;
NS_ASSERTION(line->mChildCount < 10000, "bad inline count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad inline count");
line->MarkDirty(); line->MarkDirty();
firstChild->GetNextSibling(&line->mFirstChild); firstChild->GetNextSibling(&line->mFirstChild);
} }
@ -6158,7 +6244,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
} }
} }
} }
NS_ASSERTION(line->mChildCount < 10000, "bad line child count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line child count");
count += line->mChildCount; count += line->mChildCount;
line = line->mNext; line = line->mNext;
} }

Просмотреть файл

@ -50,6 +50,8 @@
#include "nsIFocusTracker.h" #include "nsIFocusTracker.h"
#include "nsIFrameSelection.h" #include "nsIFrameSelection.h"
#define MAX_LINE_COUNT 50000
// XXX HTML:P's that are empty yet have style indicating they should // XXX HTML:P's that are empty yet have style indicating they should
// clear floaters - we need to ignore the clear behavior. // clear floaters - we need to ignore the clear behavior.
@ -203,8 +205,7 @@ public:
nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics);
nsLineLayout* aLineLayout);
~nsBlockReflowState(); ~nsBlockReflowState();
@ -240,9 +241,11 @@ public:
#endif #endif
} }
void InitFloater(nsPlaceholderFrame* aPlaceholderFrame); void InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame);
void AddFloater(nsPlaceholderFrame* aPlaceholderFrame, void AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholderFrame,
PRBool aInitialReflow); PRBool aInitialReflow);
PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats); PRBool CanPlaceFloater(const nsRect& aFloaterRect, PRUint8 aFloats);
@ -302,13 +305,20 @@ public:
nscoord* aTopMarginResult, nscoord* aTopMarginResult,
nscoord* aBottomMarginResult); nscoord* aBottomMarginResult);
void ComputeBlockAvailSpace(nsSplittableType aSplitType, nsRect& aResult); void ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult);
void RecoverStateFrom(nsLineBox* aLine, void RecoverStateFrom(nsLineBox* aLine,
PRBool aApplyTopMargin, PRBool aApplyTopMargin,
nscoord aDeltaY, nscoord aDeltaY,
nsRect* aDamageRect); nsRect* aDamageRect);
void AdvanceToNextLine() {
mLineNumber++;
}
//---------------------------------------- //----------------------------------------
// This state is the "global" state computed once for the reflow of // This state is the "global" state computed once for the reflow of
@ -321,7 +331,7 @@ public:
const nsHTMLReflowState& mReflowState; const nsHTMLReflowState& mReflowState;
nsLineLayout* mLineLayout; // nsLineLayout* mLineLayout;
nsISpaceManager* mSpaceManager; nsISpaceManager* mSpaceManager;
@ -411,18 +421,22 @@ public:
PRBool mComputeMaxElementSize; PRBool mComputeMaxElementSize;
nsSize mMaxElementSize; nsSize mMaxElementSize;
nscoord mMinLineHeight;
PRInt32 mLineNumber;
}; };
// XXX This is vile. Make it go away // XXX This is vile. Make it go away
void void
nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame) nsLineLayout::InitFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->InitFloater(aFrame); mBlockRS->InitFloater(*this, aFrame);
} }
void void
nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame) nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
{ {
mBlockRS->AddFloater(aFrame, PR_FALSE); mBlockRS->AddFloater(*this, aFrame, PR_FALSE);
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
@ -430,8 +444,7 @@ nsLineLayout::AddFloater(nsPlaceholderFrame* aFrame)
nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState, nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
nsIPresContext* aPresContext, nsIPresContext* aPresContext,
nsBlockFrame* aFrame, nsBlockFrame* aFrame,
const nsHTMLReflowMetrics& aMetrics, const nsHTMLReflowMetrics& aMetrics)
nsLineLayout* aLineLayout)
: mBlock(aFrame), : mBlock(aFrame),
mPresContext(aPresContext), mPresContext(aPresContext),
mReflowState(aReflowState), mReflowState(aReflowState),
@ -439,10 +452,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
mIsBottomMarginRoot(PR_FALSE), mIsBottomMarginRoot(PR_FALSE),
mApplyTopMargin(PR_FALSE), mApplyTopMargin(PR_FALSE),
mNextRCFrame(nsnull), mNextRCFrame(nsnull),
mPrevBottomMargin(0) mPrevBottomMargin(0),
mLineNumber(0)
{ {
mLineLayout = aLineLayout;
mSpaceManager = aReflowState.mSpaceManager; mSpaceManager = aReflowState.mSpaceManager;
// Translate into our content area and then save the // Translate into our content area and then save the
@ -515,7 +527,7 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
break; break;
} }
mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;; mComputeMaxElementSize = nsnull != aMetrics.maxElementSize;
mMaxElementSize.SizeTo(0, 0); mMaxElementSize.SizeTo(0, 0);
if (0 != borderPadding.top) { if (0 != borderPadding.top) {
@ -524,6 +536,9 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
if (0 != borderPadding.bottom) { if (0 != borderPadding.bottom) {
mIsBottomMarginRoot = PR_TRUE; mIsBottomMarginRoot = PR_TRUE;
} }
mMinLineHeight = nsHTMLReflowState::CalcLineHeight(*mPresContext,
aReflowState.frame);
} }
nsBlockReflowState::~nsBlockReflowState() nsBlockReflowState::~nsBlockReflowState()
@ -537,7 +552,9 @@ nsBlockReflowState::~nsBlockReflowState()
// at the current Y coordinate. This method assumes that // at the current Y coordinate. This method assumes that
// GetAvailableSpace has already been called. // GetAvailableSpace has already been called.
void void
nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType, nsBlockReflowState::ComputeBlockAvailSpace(nsIFrame* aFrame,
nsSplittableType aSplitType,
const nsStyleDisplay* aDisplay,
nsRect& aResult) nsRect& aResult)
{ {
nscoord availHeight = mUnconstrainedHeight nscoord availHeight = mUnconstrainedHeight
@ -546,7 +563,28 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
const nsMargin& borderPadding = BorderPadding(); const nsMargin& borderPadding = BorderPadding();
nscoord availX, availWidth; nscoord availX, availWidth;
if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
if (NS_STYLE_DISPLAY_LIST_ITEM == aDisplay->mDisplay) {
// XXX This is a hack until the css2 folks can figure how to deal
// with list-items and floaters.
nscoord leftMargin = 0;
#if 0
if (mAvailSpaceRect.x != borderPadding.left) {
// When a list-item is impacted by a left floater, slide it over
// by the right amount so that the bullets will (hopefully) be
// visible.
float p2t;
mPresContext->GetScaledPixelsToTwips(&p2t);
leftMargin = NSIntPixelsToTwips(40, p2t);
}
#endif
// Assume the frame is clueless about the space manager and only
// give it free space.
availX = mAvailSpaceRect.x + borderPadding.left + leftMargin;
availWidth = mAvailSpaceRect.width - leftMargin;
}
else if (NS_FRAME_SPLITTABLE_NON_RECTANGULAR == aSplitType) {
// Frames that know how to do non-rectangular splitting are given // Frames that know how to do non-rectangular splitting are given
// the entire available space, including space consumed by // the entire available space, including space consumed by
// floaters. // floaters.
@ -561,6 +599,7 @@ nsBlockReflowState::ComputeBlockAvailSpace(nsSplittableType aSplitType,
availX = mAvailSpaceRect.x + borderPadding.left; availX = mAvailSpaceRect.x + borderPadding.left;
availWidth = mAvailSpaceRect.width; availWidth = mAvailSpaceRect.width;
} }
aResult.SetRect(availX, mY, availWidth, availHeight); aResult.SetRect(availX, mY, availWidth, availHeight);
} }
@ -630,10 +669,13 @@ nsBlockReflowState::RecoverVerticalMargins(nsLineBox* aLine,
// Setup reflow state to compute the block childs top and bottom // Setup reflow state to compute the block childs top and bottom
// margins // margins
nsIFrame* frame = aLine->mFirstChild; nsIFrame* frame = aLine->mFirstChild;
nsRect availSpaceRect;
const nsStyleDisplay* display;
frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) display);
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpaceRect; ComputeBlockAvailSpace(frame, splitType, display, availSpaceRect);
ComputeBlockAvailSpace(splitType, availSpaceRect);
nsSize availSpace(availSpaceRect.width, availSpaceRect.height); nsSize availSpace(availSpaceRect.width, availSpaceRect.height);
nsHTMLReflowState reflowState(*mPresContext, mReflowState, nsHTMLReflowState reflowState(*mPresContext, mReflowState,
frame, availSpace); frame, availSpace);
@ -1150,11 +1192,21 @@ nsBlockFrame::Reflow(nsIPresContext& aPresContext,
const nsHTMLReflowState& aReflowState, const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus) nsReflowStatus& aStatus)
{ {
nsLineLayout lineLayout(aPresContext, aReflowState.mSpaceManager, if (IsFrameTreeTooDeep(aReflowState, aMetrics)) {
&aReflowState, nsnull != aMetrics.maxElementSize); #ifdef DEBUG_kipp
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics, {
&lineLayout); extern char* nsPresShell_ReflowStackPointerTop;
lineLayout.Init(&state); char marker;
char* newsp = (char*) &marker;
printf("XXX: frame tree is too deep; approx stack size = %d\n",
nsPresShell_ReflowStackPointerTop - newsp);
}
#endif
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
nsBlockReflowState state(aReflowState, &aPresContext, this, aMetrics);
if (NS_BLOCK_MARGIN_ROOT & mFlags) { if (NS_BLOCK_MARGIN_ROOT & mFlags) {
state.mIsTopMarginRoot = PR_TRUE; state.mIsTopMarginRoot = PR_TRUE;
state.mIsBottomMarginRoot = PR_TRUE; state.mIsBottomMarginRoot = PR_TRUE;
@ -1887,9 +1939,6 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
nsresult rv = NS_OK; nsresult rv = NS_OK;
PRBool keepGoing = PR_TRUE; PRBool keepGoing = PR_TRUE;
// Inform line layout of where the text runs are
aState.mLineLayout->SetReflowTextRuns(mTextRuns);
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
if (aState.mReflowState.reason == eReflowReason_Incremental) { if (aState.mReflowState.reason == eReflowReason_Incremental) {
nsIReflowCommand::ReflowType type; nsIReflowCommand::ReflowType type;
@ -1980,7 +2029,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
// Pull data from a next-in-flow if we can // Pull data from a next-in-flow if we can
@ -2049,13 +2098,18 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// If this is an inline frame then its time to stop // If this is an inline frame then its time to stop
aState.mPrevLine = line; aState.mPrevLine = line;
line = line->mNext; line = line->mNext;
aState.mLineLayout->AdvanceToNextLine(); aState.AdvanceToNextLine();
} }
} }
// Handle an odd-ball case: a list-item with no lines // Handle an odd-ball case: a list-item with no lines
if (mBullet && HaveOutsideBullet() && !mLines) { if (mBullet && HaveOutsideBullet() && !mLines) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
} }
#ifdef NOISY_INCREMENTAL_REFLOW #ifdef NOISY_INCREMENTAL_REFLOW
@ -2289,8 +2343,9 @@ nsBlockFrame::PullFrame(nsBlockReflowState& aState,
NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock"); NS_ASSERTION(aLine->CheckIsBlock(), "bad line isBlock");
NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters"); NS_ASSERTION(nsnull == aLine->mFloaters, "bad line floaters");
} }
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (0 != --fromLine->mChildCount) { if (0 != --fromLine->mChildCount) {
NS_ASSERTION(fromLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(fromLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
// Mark line dirty now that we pulled a child // Mark line dirty now that we pulled a child
fromLine->MarkDirty(); fromLine->MarkDirty();
frame->GetNextSibling(&fromLine->mFirstChild); frame->GetNextSibling(&fromLine->mFirstChild);
@ -2585,6 +2640,38 @@ nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState,
return PR_FALSE; return PR_FALSE;
} }
#if 0
const nsStyleText* styleText;
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
if ((NS_STYLE_WHITESPACE_PRE == styleText->mWhiteSpace) ||
(NS_STYLE_WHITESPACE_MOZ_PRE_WRAP == styleText->mWhiteSpace)) {
// Since whitespace is significant, we know that the paragraph
// is not empty (even if it has no text in it because it has
return PR_FALSE;
static PRBool
IsEmptyHTMLParagraph(nsIFrame* aFrame)
{
nsBlockFrame* bf;
if (NS_SUCCEEDED(aRS.frame->QueryInterface(kBlockFrameCID, (void**)&bf)) &&
nsBlockReflowContext::IsHTMLParagraph(aFrame)) {
if (!bf->mLines) {
// It's an html paragraph and it's empty
return PR_TRUE;
}
nsLineBox* line = bf->mLines;
while (line) {
if (!IsEmptyLine(line)) {
return PR_FALSE;
}
line = line->mNext;
}
}
return PR_FALSE;
}
#endif
nsIFrame* nsIFrame*
nsBlockFrame::GetTopBlockChild() nsBlockFrame::GetTopBlockChild()
{ {
@ -2650,52 +2737,6 @@ nsBlockFrame::GetTopBlockChild()
return nsnull; return nsnull;
} }
static void ComputeCombinedArea(const nsRect aRect1, const nsRect aRect2, nsRect& aOutRect)
{
// Rect 1's top left point: (aRect.x, aRect1.y)
// Rect 2's top left point: (aRect2.x, aRect2.y)
// Rect 2's bottom right point: (x2, y2)
// Output rect's top left point: (aOutRect.x, aOutRect.y)
// Output rect's bottom right point: (xOut, yOut)
//
// Calculate the top left point of the output rect
//
// Initialize output rect's top left point to Rect 1's top left point
aOutRect.x = aRect1.x;
aOutRect.y = aRect1.y;
if (aRect2.x < aRect1.x) {
aOutRect.x = aRect2.x;
}
if (aRect2.y < aRect1.y) {
aOutRect.y = aRect2.y;
}
//
// Calculate the bottom right point of the output rect
//
// Initialize output rect's bottom right point to Rect 1's bottom right point
nscoord xOut = aRect1.x + aRect1.width;
nscoord yOut = aRect1.y + aRect1.height;
// Initialize Rect 2's bottom right point
nscoord x2 = aRect2.x + aRect2.width;
nscoord y2 = aRect2.y + aRect2.height;
if (x2 > xOut) {
xOut = x2;
}
if (y2 > yOut) {
yOut = y2;
}
//
// Set the width and height on the output rect.
//
aOutRect.width = xOut - aOutRect.x;
aOutRect.height = yOut - aOutRect.y;
}
nsresult nsresult
nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsLineBox* aLine, nsLineBox* aLine,
@ -2739,7 +2780,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE; nsSplittableType splitType = NS_FRAME_NOT_SPLITTABLE;
frame->IsSplittable(splitType); frame->IsSplittable(splitType);
nsRect availSpace; nsRect availSpace;
aState.ComputeBlockAvailSpace(splitType, availSpace); aState.ComputeBlockAvailSpace(frame, splitType, display, availSpace);
// Reflow the block into the available space // Reflow the block into the available space
nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE; nsReflowStatus frameReflowStatus=NS_FRAME_COMPLETE;
@ -2802,7 +2843,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// Do not count the continuation child on the line it used // Do not count the continuation child on the line it used
// to be on // to be on
aLine->mChildCount--; aLine->mChildCount--;
NS_ASSERTION(aLine->mChildCount < 10000, "bad line count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line count");
} }
// Advance to next line since some of the block fit. That way // Advance to next line since some of the block fit. That way
@ -2885,11 +2926,6 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
bbox.y = aState.BorderPadding().top + ascent - bbox.y = aState.BorderPadding().top + ascent -
metrics.ascent + topMargin; metrics.ascent + topMargin;
mBullet->SetRect(bbox); mBullet->SetRect(bbox);
// Fix for bug 8314. Include the bullet's area in the combined area
// of the current line.
ComputeCombinedArea((const nsRect) bbox, (const nsRect) aLine->mCombinedArea,
aLine->mCombinedArea);
} }
} }
else { else {
@ -2929,134 +2965,197 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
#ifdef DEBUG #ifdef DEBUG
PRInt32 spins = 0; PRInt32 spins = 0;
#endif #endif
for (;;) { PRUint8 lineReflowStatus = LINE_REFLOW_REDO;
if (nsnull != aLine->mFloaters) { while (LINE_REFLOW_REDO == lineReflowStatus) {
// Forget all of the floaters on the line // Prevent overflowing limited thread stacks by creating
aLine->mFloaters->Clear(); // nsLineLayout from the heap when the frame tree depth gets
} // large.
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0); if (aState.mReflowState.mReflowDepth > 30) {//XXX layout-tune.h?
aState.mPendingFloaters.Clear(); rv = DoReflowInlineFramesMalloc(aState, aLine, aKeepReflowGoing,
&lineReflowStatus);
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
} }
else { else {
/* XXX get the height right! */ rv = DoReflowInlineFramesAuto(aState, aLine, aKeepReflowGoing,
availHeight = aState.mAvailSpaceRect.height; &lineReflowStatus);
} }
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount(); if (NS_FAILED(rv)) {
nsLineLayout* lineLayout = aState.mLineLayout; break;
lineLayout->BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == lineLayout->GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
lineLayout->SetFirstLetterStyleOK(PR_TRUE);
} }
// Reflow the frames that are already on the line first
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLine, frame, &lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
lineLayout->EndLineReflow();
#ifdef DEBUG #ifdef DEBUG
spins++; spins++;
if (1000 == spins) { if (1000 == spins) {
ListTag(stdout); ListTag(stdout);
printf(": yikes! spinning on a line over 1000 times!\n"); printf(": yikes! spinning on a line over 1000 times!\n");
NS_ABORT(); NS_ABORT();
}
#endif
continue;
} }
#endif
}
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesMalloc(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout* ll = new nsLineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
if (!ll) {
return NS_ERROR_OUT_OF_MEMORY;
}
ll->Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
ll->SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, *ll, aLine, aKeepReflowGoing,
aLineReflowStatus);
ll->EndLineReflow();
delete ll;
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFramesAuto(nsBlockReflowState& aState,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
nsLineLayout lineLayout(*aState.mPresContext,
aState.mReflowState.mSpaceManager,
&aState.mReflowState,
aState.mComputeMaxElementSize);
lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber);
lineLayout.SetReflowTextRuns(mTextRuns);
nsresult rv = DoReflowInlineFrames(aState, lineLayout, aLine,
aKeepReflowGoing, aLineReflowStatus);
lineLayout.EndLineReflow();
return rv;
}
nsresult
nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine,
PRBool* aKeepReflowGoing,
PRUint8* aLineReflowStatus)
{
if (nsnull != aLine->mFloaters) {
// Forget all of the floaters on the line
aLine->mFloaters->Clear();
}
aState.mFloaterCombinedArea.SetRect(0, 0, 0, 0);
aState.mPendingFloaters.Clear();
// Setup initial coordinate system for reflowing the inline frames
// into. Apply a previous block frame's bottom margin first.
aState.mY += aState.mPrevBottomMargin;
aState.GetAvailableSpace();
const nsMargin& borderPadding = aState.BorderPadding();
nscoord x = aState.mAvailSpaceRect.x + borderPadding.left;
nscoord availWidth = aState.mAvailSpaceRect.width;
nscoord availHeight;
if (aState.mUnconstrainedHeight) {
availHeight = NS_UNCONSTRAINEDSIZE;
}
else {
/* XXX get the height right! */
availHeight = aState.mAvailSpaceRect.height;
}
PRBool impactedByFloaters = 0 != aState.mBand.GetFloaterCount();
aLineLayout.BeginLineReflow(x, aState.mY,
availWidth, availHeight,
impactedByFloaters,
PR_FALSE /*XXX isTopOfPage*/);
if ((0 == aLineLayout.GetLineNumber()) &&
(NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) {
aLineLayout.SetFirstLetterStyleOK(PR_TRUE);
}
// Reflow the frames that are already on the line first
nsresult rv = NS_OK;
PRUint8 lineReflowStatus = LINE_REFLOW_OK;
PRInt32 i;
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < aLine->ChildCount(); i++) {
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (LINE_REFLOW_OK != lineReflowStatus) {
// It is possible that one or more of next lines are empty
// (because of DeleteChildsNextInFlow). If so, delete them now
// in case we are finished.
nsLineBox* nextLine = aLine->mNext;
while ((nsnull != nextLine) && (0 == nextLine->ChildCount())) {
// XXX Is this still necessary now that DeleteChildsNextInFlow
// uses DoRemoveFrame?
aLine->mNext = nextLine->mNext;
NS_ASSERTION(nsnull == nextLine->mFirstChild, "bad empty line");
delete nextLine;
nextLine = aLine->mNext;
}
break;
}
frame->GetNextSibling(&frame);
}
// Pull frames and reflow them until we can't
while (LINE_REFLOW_OK == lineReflowStatus) {
rv = PullFrame(aState, aLine, frame);
if (NS_FAILED(rv)) {
return rv;
}
if (nsnull == frame) {
break;
}
while (LINE_REFLOW_OK == lineReflowStatus) {
PRInt32 oldCount = aLine->ChildCount();
rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame,
&lineReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
if (aLine->ChildCount() != oldCount) {
// We just created a continuation for aFrame AND its going
// to end up on this line (e.g. :first-letter
// situation). Therefore we have to loop here before trying
// to pull another frame.
frame->GetNextSibling(&frame);
}
else {
break;
}
}
}
if (LINE_REFLOW_REDO == lineReflowStatus) {
// This happens only when we have a line that is impacted by
// floaters and the first element in the line doesn't fit with
// the floaters.
//
// What we do is to advance past the first floater we find and
// then reflow the line all over again.
NS_ASSERTION(aState.mBand.GetFloaterCount(),
"redo line on totally empty line");
NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mAvailSpaceRect.height,
"unconstrained height on totally empty line");
aState.mY += aState.mAvailSpaceRect.height;
// XXX: a small optimization can be done here when paginating:
// if the new Y coordinate is past the end of the block then
// push the line and return now instead of later on after we are
// past the floater.
}
else {
// If we are propogating out a break-before status then there is // If we are propogating out a break-before status then there is
// no point in placing the line. // no point in placing the line.
NS_ASSERTION(aLine->mChildCount < 10000, "bad line child count"); NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
if (NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) {
lineLayout->EndLineReflow(); rv = PlaceLine(aState, aLineLayout, aLine, aKeepReflowGoing);
} }
else {
rv = PlaceLine(aState, aLine, aKeepReflowGoing);
}
break;
} }
*aLineReflowStatus = lineReflowStatus;
return rv; return rv;
} }
@ -3071,6 +3170,7 @@ nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState,
*/ */
nsresult nsresult
nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame, nsIFrame* aFrame,
PRUint8* aLineReflowStatus) PRUint8* aLineReflowStatus)
@ -3079,7 +3179,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// If it's currently ok to be reflowing in first-letter style then // If it's currently ok to be reflowing in first-letter style then
// we must be about to reflow a frame that has first-letter style. // we must be about to reflow a frame that has first-letter style.
PRBool reflowingFirstLetter = aState.mLineLayout->GetFirstLetterStyleOK(); PRBool reflowingFirstLetter = aLineLayout.GetFirstLetterStyleOK();
#ifdef NOISY_FIRST_LETTER #ifdef NOISY_FIRST_LETTER
ListTag(stdout); ListTag(stdout);
printf(": reflowing "); printf(": reflowing ");
@ -3088,9 +3188,8 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
#endif #endif
// Reflow the inline frame // Reflow the inline frame
nsLineLayout* lineLayout = aState.mLineLayout;
nsReflowStatus frameReflowStatus; nsReflowStatus frameReflowStatus;
nsresult rv = lineLayout->ReflowFrame(aFrame, &aState.mNextRCFrame, nsresult rv = aLineLayout.ReflowFrame(aFrame, &aState.mNextRCFrame,
frameReflowStatus); frameReflowStatus);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
@ -3135,7 +3234,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
else { else {
// It's not the first child on this line so go ahead and split // It's not the first child on this line so go ahead and split
// the line. We will see the frame again on the next-line. // the line. We will see the frame again on the next-line.
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3157,7 +3256,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line, but after the frame just reflowed // Split line, but after the frame just reflowed
nsIFrame* nextFrame; nsIFrame* nextFrame;
aFrame->GetNextSibling(&nextFrame); aFrame->GetNextSibling(&nextFrame);
rv = SplitLine(aState, aLine, nextFrame); rv = SplitLine(aState, aLineLayout, aLine, nextFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3203,7 +3302,7 @@ nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState,
// Split line after the current frame // Split line after the current frame
*aLineReflowStatus = LINE_REFLOW_STOP; *aLineReflowStatus = LINE_REFLOW_STOP;
aFrame->GetNextSibling(&aFrame); aFrame->GetNextSibling(&aFrame);
rv = SplitLine(aState, aLine, aFrame); rv = SplitLine(aState, aLineLayout, aLine, aFrame);
if (NS_FAILED(rv)) { if (NS_FAILED(rv)) {
return rv; return rv;
} }
@ -3241,6 +3340,7 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
if (nsnull != nextInFlow) { if (nsnull != nextInFlow) {
aMadeNewFrame = PR_TRUE; aMadeNewFrame = PR_TRUE;
aLine->mChildCount++; aLine->mChildCount++;
NS_ASSERTION(aLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_FALSE); VerifyLines(PR_FALSE);
@ -3250,11 +3350,11 @@ nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState,
nsresult nsresult
nsBlockFrame::SplitLine(nsBlockReflowState& aState, nsBlockFrame::SplitLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
nsIFrame* aFrame) nsIFrame* aFrame)
{ {
nsLineLayout* lineLayout = aState.mLineLayout; PRInt32 pushCount = aLine->ChildCount() - aLineLayout.GetCurrentSpanCount();
PRInt32 pushCount = aLine->ChildCount() - lineLayout->GetCurrentSpanCount();
NS_ASSERTION(pushCount >= 0, "bad push count"); NS_ASSERTION(pushCount >= 0, "bad push count");
//printf("BEFORE (pushCount=%d):\n", pushCount); //printf("BEFORE (pushCount=%d):\n", pushCount);
//aLine->List(stdout, 0); //aLine->List(stdout, 0);
@ -3294,7 +3394,7 @@ nsBlockFrame::SplitLine(nsBlockReflowState& aState,
// Let line layout know that some frames are no longer part of its // Let line layout know that some frames are no longer part of its
// state. // state.
if (!aLine->IsBlock()) { if (!aLine->IsBlock()) {
lineLayout->SplitLineTo(aLine->ChildCount()); aLineLayout.SplitLineTo(aLine->ChildCount());
} }
#ifdef DEBUG #ifdef DEBUG
VerifyLines(PR_TRUE); VerifyLines(PR_TRUE);
@ -3340,6 +3440,7 @@ nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState, nsLineBox* aLine)
nsresult nsresult
nsBlockFrame::PlaceLine(nsBlockReflowState& aState, nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
nsLineLayout& aLineLayout,
nsLineBox* aLine, nsLineBox* aLine,
PRBool* aKeepReflowGoing) PRBool* aKeepReflowGoing)
{ {
@ -3360,15 +3461,16 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// method is used for placing a line of inline frames. If the rare // method is used for placing a line of inline frames. If the rare
// case is happening then the worst that will happen is that the // case is happening then the worst that will happen is that the
// bullet frame will be reflowed twice. // bullet frame will be reflowed twice.
nsLineLayout* lineLayout = aState.mLineLayout;
PRBool addedBullet = PR_FALSE; PRBool addedBullet = PR_FALSE;
if (HaveOutsideBullet() && (aLine == mLines) && if (HaveOutsideBullet() && (aLine == mLines) &&
(!lineLayout->IsZeroHeight() || !aLine->mNext)) { (!aLineLayout.IsZeroHeight() || !aLine->mNext)) {
PlaceBullet(aState); nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
aLineLayout.AddBulletFrame(mBullet, metrics);
addedBullet = PR_TRUE; addedBullet = PR_TRUE;
} }
nsSize maxElementSize; nsSize maxElementSize;
lineLayout->VerticalAlignFrames(aLine->mBounds, maxElementSize); aLineLayout.VerticalAlignFrames(aLine->mBounds, maxElementSize);
#ifdef DEBUG #ifdef DEBUG
{ {
static nscoord lastHeight = 0; static nscoord lastHeight = 0;
@ -3397,11 +3499,11 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
#else #else
PRBool allowJustify = PR_FALSE; PRBool allowJustify = PR_FALSE;
#endif #endif
lineLayout->TrimTrailingWhiteSpace(aLine->mBounds); aLineLayout.TrimTrailingWhiteSpace(aLine->mBounds);
lineLayout->HorizontalAlignFrames(aLine->mBounds, allowJustify); aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
lineLayout->RelativePositionFrames(aLine->mCombinedArea); aLineLayout.RelativePositionFrames(aLine->mCombinedArea);
if (addedBullet) { if (addedBullet) {
lineLayout->RemoveBulletFrame(mBullet); aLineLayout.RemoveBulletFrame(mBullet);
} }
// Inline lines do not have margins themselves; however they are // Inline lines do not have margins themselves; however they are
@ -3443,7 +3545,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE; aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
*aKeepReflowGoing = PR_FALSE; *aKeepReflowGoing = PR_FALSE;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -3491,8 +3592,6 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
break; break;
} }
lineLayout->EndLineReflow();
return rv; return rv;
} }
@ -4052,6 +4151,7 @@ nsBlockFrame::AddFrames(nsIPresContext* aPresContext,
else { else {
prevSibLine->mChildCount++; prevSibLine->mChildCount++;
prevSibLine->MarkDirty(); prevSibLine->MarkDirty();
NS_ASSERTION(prevSibLine->mChildCount < MAX_LINE_COUNT, "bad line child count");
} }
aPrevSibling = newFrame; aPrevSibling = newFrame;
@ -4400,7 +4500,7 @@ nsBlockFrame::DoRemoveFrame(nsIPresContext* aPresContext,
line = next; line = next;
} }
else { else {
NS_ASSERTION(line->mChildCount < 10000, "bad line count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line count");
// Make the line that just lost a frame dirty // Make the line that just lost a frame dirty
line->MarkDirty(); line->MarkDirty();
@ -4654,7 +4754,8 @@ nsBlockFrame::ReflowFloater(nsBlockReflowState& aState,
} }
void void
nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder) nsBlockReflowState::InitFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder)
{ {
// Set the geometric parent of the floater // Set the geometric parent of the floater
nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame(); nsIFrame* floater = aPlaceholder->GetOutOfFlowFrame();
@ -4662,7 +4763,7 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// Then add the floater to the current line and place it when // Then add the floater to the current line and place it when
// appropriate // appropriate
AddFloater(aPlaceholder, PR_TRUE); AddFloater(aLineLayout, aPlaceholder, PR_TRUE);
} }
// This is called by the line layout's AddFloater method when a // This is called by the line layout's AddFloater method when a
@ -4671,7 +4772,8 @@ nsBlockReflowState::InitFloater(nsPlaceholderFrame* aPlaceholder)
// then the floater is place immediately, otherwise the floater // then the floater is place immediately, otherwise the floater
// placement is deferred until the line has been reflowed. // placement is deferred until the line has been reflowed.
void void
nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder, nsBlockReflowState::AddFloater(nsLineLayout& aLineLayout,
nsPlaceholderFrame* aPlaceholder,
PRBool aInitialReflow) PRBool aInitialReflow)
{ {
NS_PRECONDITION(nsnull != mCurrentLine, "null ptr"); NS_PRECONDITION(nsnull != mCurrentLine, "null ptr");
@ -4684,7 +4786,7 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Now place the floater immediately if possible. Otherwise stash it // Now place the floater immediately if possible. Otherwise stash it
// away in mPendingFloaters and place it later. // away in mPendingFloaters and place it later.
if (mLineLayout->CanPlaceFloaterNow()) { if (aLineLayout.CanPlaceFloaterNow()) {
nsRect combinedArea; nsRect combinedArea;
nsMargin floaterMargins; nsMargin floaterMargins;
nsMargin floaterOffsets; nsMargin floaterOffsets;
@ -4713,10 +4815,10 @@ nsBlockReflowState::AddFloater(nsPlaceholderFrame* aPlaceholder,
// Pass on updated available space to the current inline reflow engine // Pass on updated available space to the current inline reflow engine
GetAvailableSpace(); GetAvailableSpace();
mLineLayout->UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY, aLineLayout.UpdateBand(mAvailSpaceRect.x + BorderPadding().left, mY,
mAvailSpaceRect.width, mAvailSpaceRect.width,
mAvailSpaceRect.height, mAvailSpaceRect.height,
isLeftFloater); isLeftFloater);
// Restore coordinate system // Restore coordinate system
mSpaceManager->Translate(dx, dy); mSpaceManager->Translate(dx, dy);
@ -5138,6 +5240,10 @@ nsBlockFrame::Paint(nsIPresContext& aPresContext,
const nsRect& aDirtyRect, const nsRect& aDirtyRect,
nsFramePaintLayer aWhichLayer) nsFramePaintLayer aWhichLayer)
{ {
if (NS_FRAME_IS_UNFLOWABLE & mState) {
return NS_OK;
}
#ifdef NOISY_DAMAGE_REPAIR #ifdef NOISY_DAMAGE_REPAIR
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) { if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
PRInt32 depth = GetDepth(); PRInt32 depth = GetDepth();
@ -5804,7 +5910,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
availSize.height = NS_UNCONSTRAINEDSIZE; availSize.height = NS_UNCONSTRAINEDSIZE;
nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState, nsHTMLReflowState reflowState(*aState.mPresContext, aState.mReflowState,
mBullet, availSize); mBullet, availSize);
reflowState.mLineLayout = aState.mLineLayout;
nsIHTMLReflow* htmlReflow; nsIHTMLReflow* htmlReflow;
nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow); nsresult rv = mBullet->QueryInterface(kIHTMLReflowIID, (void**)&htmlReflow);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
@ -5825,25 +5930,6 @@ nsBlockFrame::ReflowBullet(nsBlockReflowState& aState,
mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height)); mBullet->SetRect(nsRect(x, y, aMetrics.width, aMetrics.height));
} }
void
nsBlockFrame::PlaceBullet(nsBlockReflowState& aState)
{
// First reflow the bullet
nsLineLayout* lineLayout = aState.mLineLayout;
nsHTMLReflowMetrics metrics(nsnull);
ReflowBullet(aState, metrics);
if (mLines) {
// If we have at least one line then let line-layout position the
// bullet (this way the bullet is vertically aligned properly).
lineLayout->AddBulletFrame(mBullet, metrics);
}
else {
// There are no lines so we have to fake up some y motion so that
// we end up with *some* height.
aState.mY += metrics.height;
}
}
//XXX get rid of this -- its slow //XXX get rid of this -- its slow
void void
nsBlockFrame::BuildFloaterList() nsBlockFrame::BuildFloaterList()
@ -6054,7 +6140,7 @@ nsAnonymousBlockFrame::RemoveFirstFrame()
else { else {
// Remove frame from line and mark the line dirty // Remove frame from line and mark the line dirty
--line->mChildCount; --line->mChildCount;
NS_ASSERTION(line->mChildCount < 10000, "bad inline count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad inline count");
line->MarkDirty(); line->MarkDirty();
firstChild->GetNextSibling(&line->mFirstChild); firstChild->GetNextSibling(&line->mFirstChild);
} }
@ -6158,7 +6244,7 @@ nsBlockFrame::VerifyLines(PRBool aFinalCheckOK)
} }
} }
} }
NS_ASSERTION(line->mChildCount < 10000, "bad line child count"); NS_ASSERTION(line->mChildCount < MAX_LINE_COUNT, "bad line child count");
count += line->mChildCount; count += line->mChildCount;
line = line->mNext; line = line->mNext;
} }