Clean up max-element-size computations; added code to remove empty lines (disabled for now)

git-svn-id: svn://10.0.0.236/trunk@16582 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
kipp%netscape.com
1998-12-17 18:52:10 +00:00
parent 0dcc919b64
commit c5f1d3ba49
8 changed files with 2104 additions and 1312 deletions

View File

@@ -53,7 +53,7 @@ static NS_DEFINE_IID(kITextContentIID, NS_ITEXT_CONTENT_IID);/* XXX */
#undef NOISY_FIRST_LINE
#undef REALLY_NOISY_FIRST_LINE
#undef NOISY_FIRST_LETTER
#undef NOISY_MAX_ELEMENT_SIZE
#define NOISY_MAX_ELEMENT_SIZE
#undef NOISY_RUNIN
#undef NOISY_FLOATER_CLEARING
#undef NOISY_INCREMENTAL_REFLOW
@@ -773,6 +773,7 @@ nsBaseIBFrame::ComputeFinalSize(nsBlockReflowState& aState,
// Special check for zero sized content: If our content is zero
// sized then we collapse into nothingness.
PRBool emptyFrame = PR_FALSE;
if ((eHTMLFrameConstraint_Unconstrained == aState.widthConstraint) &&
(eHTMLFrameConstraint_Unconstrained == aState.heightConstraint) &&
((0 == aState.mKidXMost - aState.mBorderPadding.left) &&
@@ -781,6 +782,7 @@ nsBaseIBFrame::ComputeFinalSize(nsBlockReflowState& aState,
aMetrics.height = 0;
aState.mAscent = 0;
aState.mDescent = 0;
emptyFrame = PR_TRUE;
}
if (0 == (BLOCK_IS_INLINE & mFlags)) {
@@ -792,83 +794,29 @@ nsBaseIBFrame::ComputeFinalSize(nsBlockReflowState& aState,
aMetrics.descent = aState.mDescent;
}
// XXX this needs ALOT OF REWORKING
if (aState.mComputeMaxElementSize) {
nscoord maxWidth, maxHeight;
if (aState.mNoWrap) {
maxWidth = 0;
maxHeight = 0;
// Find the maximum line-box width and use that as the
// max-element width
nsLineBox* line = mLines;
while (nsnull != line) {
nscoord xm = line->mBounds.XMost();
if (xm > maxWidth) {
maxWidth = xm;
}
line = line->mNext;
}
// XXX winging it!
maxHeight = aState.mMaxElementSize.height +
aState.mBorderPadding.top + aState.mBorderPadding.bottom;
if (emptyFrame) {
// When a frame is empty it must not provide any
// max-element-size information.
maxWidth = maxHeight = 0;
}
else {
maxWidth = aState.mMaxElementSize.width +
aState.mBorderPadding.left + aState.mBorderPadding.right;
if (aState.mNoWrap) {
// When no-wrap is true the max-element-size.width is the
// width of the widest line plus the right border. Note that
// aState.mKidXMost already has the left border factored into
// it
maxWidth = aState.mKidXMost + aState.mBorderPadding.right;
}
else {
// Add in border and padding dimensions to already computed
// max-element-size values.
maxWidth = aState.mMaxElementSize.width +
aState.mBorderPadding.left + aState.mBorderPadding.right;
}
maxHeight = aState.mMaxElementSize.height +
aState.mBorderPadding.top + aState.mBorderPadding.bottom;
// XXX This is still an approximation of the truth:
// 1. it doesn't take into account that after a floater is
// placed we always place at least *something* on the line.
// 2. if a floater doesn't start at the left margin (because
// it's placed relative to a floater on a preceeding line, say),
// then we get the wrong answer.
nsLineBox* line = mLines;
while (nsnull != line) {
if (nsnull != line->mFloaters) {
nsRect r;
nsMargin floaterMargin;
// Sum up the widths of the floaters on this line
PRInt32 sum = 0;
PRInt32 n = line->mFloaters->Count();
for (PRInt32 i = 0; i < n; i++) {
nsPlaceholderFrame* placeholder = (nsPlaceholderFrame*)
line->mFloaters->ElementAt(i);
nsIFrame* floater = placeholder->GetAnchoredItem();
floater->GetRect(r);
const nsStyleDisplay* floaterDisplay;
const nsStyleSpacing* floaterSpacing;
floater->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&)floaterDisplay);
floater->GetStyleData(eStyleStruct_Spacing,
(const nsStyleStruct*&)floaterSpacing);
// Compute the margin for the floater (again!)
nsHTMLReflowState::ComputeMarginFor(floater, &aState,
floaterMargin);
nscoord width = r.width + floaterMargin.left + floaterMargin.right;
NS_ASSERTION((NS_STYLE_FLOAT_LEFT == floaterDisplay->mFloats) ||
(NS_STYLE_FLOAT_RIGHT == floaterDisplay->mFloats),
"invalid float type");
sum += width;
}
// See if we need a larger max-element width
if (sum > maxWidth) {
maxWidth = sum;
}
}
line = line->mNext;
}
}
// Store away the final value
@@ -876,8 +824,9 @@ nsBaseIBFrame::ComputeFinalSize(nsBlockReflowState& aState,
aMetrics.maxElementSize->height = maxHeight;
#ifdef NOISY_MAX_ELEMENT_SIZE
ListTag(stdout);
printf(": max-element-size:%d,%d desired:%d,%d\n",
maxWidth, maxHeight, aMetrics.width, aMetrics.height);
printf(": max-element-size:%d,%d desired:%d,%d maxSize:%d,%d\n",
maxWidth, maxHeight, aMetrics.width, aMetrics.height,
aState.maxSize.width, aState.maxSize.height);
#endif
}
@@ -938,137 +887,6 @@ printf(": => carried=%d,%d\n", aMetrics.mCarriedOutTopMargin, aMetrics.mCarriedO
#endif
}
nsresult
nsBaseIBFrame::AppendNewFrames(nsIPresContext& aPresContext,
nsIFrame* aNewFrame)
{
// Get our last line and then get its last child
nsIFrame* lastFrame;
nsLineBox* lastLine = nsLineBox::LastLine(mLines);
if (nsnull != lastLine) {
lastFrame = lastLine->LastChild();
} else {
lastFrame = nsnull;
}
// Add the new frames to the sibling list; wrap any frames that
// require wrapping
if (nsnull != lastFrame) {
lastFrame->SetNextSibling(aNewFrame);
}
nsresult rv;
// Make sure that new inlines go onto the end of the lastLine when
// the lastLine is mapping inline frames.
PRInt32 pendingInlines = 0;
if (nsnull != lastLine) {
if (!lastLine->IsBlock()) {
pendingInlines = 1;
}
}
// Now create some lines for the new frames
nsIFrame* prevFrame = lastFrame;
for (nsIFrame* frame = aNewFrame; nsnull != frame; frame->GetNextSibling(frame)) {
// See if the child is a block or non-block
const nsStyleDisplay* kidDisplay;
rv = frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) kidDisplay);
if (NS_OK != rv) {
return rv;
}
const nsStylePosition* kidPosition;
rv = frame->GetStyleData(eStyleStruct_Position,
(const nsStyleStruct*&) kidPosition);
if (NS_OK != rv) {
return rv;
}
PRBool isBlock =
nsLineLayout::TreatFrameAsBlock(kidDisplay, kidPosition);
// See if we need to move the frame outside of the flow, and insert a
// placeholder frame in its place
nsIFrame* placeholder;
if (MoveFrameOutOfFlow(aPresContext, frame, kidDisplay, kidPosition,
placeholder)) {
// Reset the previous frame's next sibling pointer
if (nsnull != prevFrame) {
prevFrame->SetNextSibling(placeholder);
}
// The placeholder frame is always inline
frame = placeholder;
isBlock = PR_FALSE;
}
else {
// Wrap the frame in a view if necessary
nsIStyleContext* kidSC;
frame->GetStyleContext(kidSC);
rv = CreateViewForFrame(aPresContext, frame, kidSC, PR_FALSE);
NS_RELEASE(kidSC);
if (NS_OK != rv) {
return rv;
}
}
// If the child is an inline then add it to the lastLine (if it's
// an inline line, otherwise make a new line). If the child is a
// block then make a new line and put the child in that line.
if (isBlock) {
// If the previous line has pending inline data to be reflowed,
// do so now.
if (0 != pendingInlines) {
// Set this to true in case we don't end up reflowing all of the
// frames on the line (because they end up being pushed).
lastLine->MarkDirty();
pendingInlines = 0;
}
// Create a line for the block
nsLineBox* line = new nsLineBox(frame, 1, LINE_IS_BLOCK);
if (nsnull == line) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (nsnull == lastLine) {
mLines = line;
}
else {
lastLine->mNext = line;
}
lastLine = line;
}
else {
// Queue up the inlines for reflow later on
if (0 == pendingInlines) {
nsLineBox* line = new nsLineBox(frame, 0, 0);
if (nsnull == line) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (nsnull == lastLine) {
mLines = line;
}
else {
lastLine->mNext = line;
}
lastLine = line;
}
lastLine->mChildCount++;
pendingInlines++;
}
// Remember the previous frame
prevFrame = frame;
}
if (0 != pendingInlines) {
// Set this to true in case we don't end up reflowing all of the
// frames on the line (because they end up being pushed).
lastLine->MarkDirty();
}
return NS_OK;
}
nsresult
nsBaseIBFrame::PrepareInitialReflow(nsBlockReflowState& aState)
{
@@ -2187,7 +2005,8 @@ nsBaseIBFrame::ReflowBlockFrame(nsBlockReflowState& aState,
nsRect availSpace(availX, aState.mY, availWidth, availHeight);
WillReflowFrame(aState, aLine, frame);
nsReflowStatus frameReflowStatus;
rv = brc.ReflowBlock(frame, availSpace, aState.IsAdjacentWithTop(), frameReflowStatus);
rv = brc.ReflowBlock(frame, availSpace, aState.IsAdjacentWithTop(),
frameReflowStatus);
if (NS_FAILED(rv)) {
return rv;
}
@@ -2730,6 +2549,8 @@ printf(" mY=%d carried=%d,%d top=%d bottom=%d prev=%d shouldApply=%s\n",
}
aState.mY = newY;
PostPlaceLine(aState, aLine, ir.GetMaxElementSize());
// Any below current line floaters to place?
if (0 != aState.mPendingFloaters.Count()) {
aState.PlaceFloaters(&aState.mPendingFloaters, PR_FALSE);
@@ -2746,14 +2567,35 @@ printf(" mY=%d carried=%d,%d top=%d bottom=%d prev=%d shouldApply=%s\n",
break;
}
PostPlaceLine(aState, aLine, ir.GetMaxElementSize());
// Notify anyone who cares that the line has been placed
DidPlaceLine(aState, aLine, topMargin, bottomMargin, aKeepReflowGoing);
return rv;
}
// Compute the line's max-element-size by adding into the raw value
// computed by reflowing the contents of the line (aMaxElementSize)
// the impact of floaters on this line or the preceeding lines.
void
nsBaseIBFrame::ComputeLineMaxElementSize(nsBlockReflowState& aState,
nsLineBox* aLine,
nsSize* aMaxElementSize)
{
nscoord maxWidth, maxHeight;
aState.mCurrentBand.GetMaxElementSize(&maxWidth, &maxHeight);
// Add in the maximum width of any floaters in the band because we
// always place some non-floating content with a floater.
aMaxElementSize->width += maxWidth;
// If the maximum-height of the tallest floater is larger than the
// maximum-height of the content then update the max-element-size
// height
if (maxHeight > aMaxElementSize->height) {
aMaxElementSize->height = maxHeight;
}
}
void
nsBaseIBFrame::PostPlaceLine(nsBlockReflowState& aState,
nsLineBox* aLine,
@@ -2761,11 +2603,17 @@ nsBaseIBFrame::PostPlaceLine(nsBlockReflowState& aState,
{
// Update max-element-size
if (aState.mComputeMaxElementSize) {
if (aMaxElementSize.width > aState.mMaxElementSize.width) {
aState.mMaxElementSize.width = aMaxElementSize.width;
nsSize lineMaxElementSize(aMaxElementSize);
if ((0 == (BLOCK_IS_INLINE & mFlags)) &&
(0 != aState.mCurrentBand.GetFloaterCount())) {
// Add in floater impacts to the lines max-element-size
ComputeLineMaxElementSize(aState, aLine, &lineMaxElementSize);
}
if (aMaxElementSize.height > aState.mMaxElementSize.height) {
aState.mMaxElementSize.height = aMaxElementSize.height;
if (lineMaxElementSize.width > aState.mMaxElementSize.width) {
aState.mMaxElementSize.width = lineMaxElementSize.width;
}
if (lineMaxElementSize.height > aState.mMaxElementSize.height) {
aState.mMaxElementSize.height = lineMaxElementSize.height;
}
}
@@ -2968,11 +2816,145 @@ nsBaseIBFrame::DrainOverflowLines()
return drained;
}
//////////////////////////////////////////////////////////////////////
// Frame list manipulation routines
nsresult
nsBaseIBFrame::AppendNewFrames(nsIPresContext& aPresContext,
nsIFrame* aNewFrame)
{
// Get our last line and then get its last child
nsIFrame* lastFrame;
nsLineBox* lastLine = nsLineBox::LastLine(mLines);
if (nsnull != lastLine) {
lastFrame = lastLine->LastChild();
} else {
lastFrame = nsnull;
}
// Add the new frames to the sibling list; wrap any frames that
// require wrapping
if (nsnull != lastFrame) {
lastFrame->SetNextSibling(aNewFrame);
}
nsresult rv;
// Make sure that new inlines go onto the end of the lastLine when
// the lastLine is mapping inline frames.
PRInt32 pendingInlines = 0;
if (nsnull != lastLine) {
if (!lastLine->IsBlock()) {
pendingInlines = 1;
}
}
// Now create some lines for the new frames
nsIFrame* prevFrame = lastFrame;
for (nsIFrame* frame = aNewFrame; nsnull != frame;
frame->GetNextSibling(frame)) {
// See if the child is a block or non-block
const nsStyleDisplay* kidDisplay;
rv = frame->GetStyleData(eStyleStruct_Display,
(const nsStyleStruct*&) kidDisplay);
if (NS_OK != rv) {
return rv;
}
const nsStylePosition* kidPosition;
rv = frame->GetStyleData(eStyleStruct_Position,
(const nsStyleStruct*&) kidPosition);
if (NS_OK != rv) {
return rv;
}
PRBool isBlock = nsLineLayout::TreatFrameAsBlock(kidDisplay, kidPosition);
// See if we need to move the frame outside of the flow, and insert a
// placeholder frame in its place
nsIFrame* placeholder;
if (MoveFrameOutOfFlow(aPresContext, frame, kidDisplay, kidPosition,
placeholder)) {
// Reset the previous frame's next sibling pointer
if (nsnull != prevFrame) {
prevFrame->SetNextSibling(placeholder);
}
// The placeholder frame is always inline
frame = placeholder;
isBlock = PR_FALSE;
}
else {
// Wrap the frame in a view if necessary
nsIStyleContext* kidSC;
frame->GetStyleContext(kidSC);
rv = CreateViewForFrame(aPresContext, frame, kidSC, PR_FALSE);
NS_RELEASE(kidSC);
if (NS_OK != rv) {
return rv;
}
}
// If the child is an inline then add it to the lastLine (if it's
// an inline line, otherwise make a new line). If the child is a
// block then make a new line and put the child in that line.
if (isBlock) {
// If the previous line has pending inline data to be reflowed,
// do so now.
if (0 != pendingInlines) {
// Set this to true in case we don't end up reflowing all of the
// frames on the line (because they end up being pushed).
lastLine->MarkDirty();
pendingInlines = 0;
}
// Create a line for the block
nsLineBox* line = new nsLineBox(frame, 1, LINE_IS_BLOCK);
if (nsnull == line) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (nsnull == lastLine) {
mLines = line;
}
else {
lastLine->mNext = line;
}
lastLine = line;
}
else {
if (0 == pendingInlines) {
nsLineBox* line = new nsLineBox(frame, 0, 0);
if (nsnull == line) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (nsnull == lastLine) {
mLines = line;
}
else {
lastLine->mNext = line;
}
lastLine = line;
}
lastLine->mChildCount++;
pendingInlines++;
}
// Remember the previous frame
prevFrame = frame;
}
if (0 != pendingInlines) {
// Set this to true in case we don't end up reflowing all of the
// frames on the line (because they end up being pushed).
lastLine->MarkDirty();
}
//XXX RemoveEmptyLines(aPresContext);
return NS_OK;
}
nsresult
nsBaseIBFrame::InsertNewFrame(nsIPresContext& aPresContext,
nsBaseIBFrame* aParentFrame,
nsIFrame* aNewFrame,
nsIFrame* aPrevSibling)
nsBaseIBFrame* aParentFrame,
nsIFrame* aNewFrame,
nsIFrame* aPrevSibling)
{
if (nsnull == mLines) {
NS_ASSERTION(nsnull == aPrevSibling, "prev-sibling and empty line list!");
@@ -3107,15 +3089,16 @@ nsBaseIBFrame::InsertNewFrame(nsIPresContext& aPresContext,
aPrevSibling->SetNextSibling(aNewFrame);
}
//XXX RemoveEmptyLines(aPresContext);
return NS_OK;
}
// XXX this code can't work...rewrite it!
nsresult
nsBaseIBFrame::RemoveFrame(nsBlockReflowState& aState,
nsBaseIBFrame* aParentFrame,
nsIFrame* aDeletedFrame,
nsIFrame* aPrevSibling)
nsBaseIBFrame* aParentFrame,
nsIFrame* aDeletedFrame,
nsIFrame* aPrevSibling)
{
// Find the line that contains deletedFrame; we also find the pointer to
// the line.
@@ -3215,6 +3198,153 @@ nsBaseIBFrame::RemoveFrame(nsBlockReflowState& aState,
return NS_OK;
}
// XXX UNUSED code to remove "empty lines" from line list.
#if XXX_use_this_hack
// XXX
#define XP_IS_SPACE(_ch) \
(((_ch) == ' ') || ((_ch) == '\t') || ((_ch) == '\n'))
#include "nsTextFragment.h"
static PRBool
IsEmptyLine(nsIPresContext& aPresContext, nsLineBox* aLine)
{
PRInt32 i, n = aLine->ChildCount();
nsIFrame* frame = aLine->mFirstChild;
for (i = 0; i < n; i++) {
nsIContent* content;
nsresult rv = frame->GetContent(content);
if (NS_FAILED(rv) || (nsnull == content)) {
// If it doesn't have any content then this can't be an empty line
return PR_FALSE;
}
nsITextContent* tc;
rv = content->QueryInterface(kITextContentIID, (void**) &tc);
if (NS_FAILED(rv) || (nsnull == tc)) {
// If it's not text content then this can't be an empty line
NS_RELEASE(content);
return PR_FALSE;
}
const nsTextFragment* frag;
PRInt32 numFrags;
rv = tc->GetText(frag, numFrags);
if (NS_FAILED(rv)) {
NS_RELEASE(content);
NS_RELEASE(tc);
return PR_FALSE;
}
// If the text has any non-whitespace characters in it then the
// line is not an empty line.
while (--numFrags >= 0) {
PRInt32 len = frag->GetLength();
if (frag->Is2b()) {
const PRUnichar* cp = frag->Get2b();
const PRUnichar* end = cp + len;
while (cp < end) {
PRUnichar ch = *cp++;
if (!XP_IS_SPACE(ch)) {
NS_RELEASE(tc);
NS_RELEASE(content);
return PR_FALSE;
}
}
}
else {
const char* cp = frag->Get1b();
const char* end = cp + len;
while (cp < end) {
char ch = *cp++;
if (!XP_IS_SPACE(ch)) {
NS_RELEASE(tc);
NS_RELEASE(content);
return PR_FALSE;
}
}
}
frag++;
}
NS_RELEASE(tc);
NS_RELEASE(content);
frame->GetNextSibling(frame);
}
return PR_TRUE;
}
PRInt32 gDeletedFrameCount;
PRInt32 gDeletedLineCount;
void
nsBaseIBFrame::RemoveEmptyLines(nsIPresContext& aPresContext)
{
// Inline frames do not have empty lines, so don't bother
if (BLOCK_IS_INLINE & mFlags) return;
// PRE-formatted content considers whitespace significant, so don't
// remove the empty frames either.
const nsStyleText* text;
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) text);
if (NS_STYLE_WHITESPACE_PRE == text->mWhiteSpace) {
return;
}
PRBool afterBlock = PR_TRUE;
nsLineBox* prevLine = nsnull;
nsLineBox* line = mLines;
nsIFrame* prevFrame = nsnull;
while (nsnull != line) {
if (line->IsBlock()) {
afterBlock = PR_TRUE;
prevFrame = line->mFirstChild;
}
else if (afterBlock) {
afterBlock = PR_FALSE;
// This is an inline line and it is immediately after a block
// (or its our first line). See if it contains nothing but
// collapsible text.
if (IsEmptyLine(aPresContext, line)) {
// Take line out of the list
nsLineBox* next = line->mNext;
if (nsnull == prevLine) {
mLines = next;
}
else {
prevLine->mNext = next;
}
// Delete the frames on the line
nsIFrame* frame = line->mFirstChild;
PRInt32 n = line->ChildCount();
while (--n >= 0) {
nsIFrame* next;
frame->GetNextSibling(next);
frame->DeleteFrame(aPresContext);
frame = next;
gDeletedFrameCount++;
if ((0 == n) && (nsnull != prevFrame)) {
prevFrame->SetNextSibling(next);
}
}
// And delete the line
gDeletedLineCount++;
delete line;
line = next;
continue;
}
}
else {
prevFrame = line->LastChild();
}
prevLine = line;
line = line->mNext;
}
}
#endif
PRBool
nsBaseIBFrame::DeleteChildsNextInFlow(nsIPresContext& aPresContext,
nsIFrame* aChild)