mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 07:05:24 +00:00
1196 lines
34 KiB
C++
1196 lines
34 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#include "nsLineLayout.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsBlockFrame.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIContentDelegate.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsISpaceManager.h"
|
|
#include "nsIPtr.h"
|
|
#include "nsAbsoluteFrame.h"
|
|
#include "nsPlaceholderFrame.h"
|
|
#include "nsCSSLayout.h"
|
|
#include "nsCRT.h"
|
|
#include "nsReflowCommand.h"
|
|
|
|
#undef NOISY_REFLOW
|
|
|
|
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);
|
|
static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
|
|
static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
|
|
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
|
|
static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
|
|
|
|
NS_DEF_PTR(nsIContent);
|
|
NS_DEF_PTR(nsIStyleContext);
|
|
|
|
nsLineData::nsLineData()
|
|
{
|
|
mNextLine = nsnull;
|
|
mPrevLine = nsnull;
|
|
mFirstChild = nsnull;
|
|
mChildCount = 0;
|
|
mFirstContentOffset = 0;
|
|
mLastContentOffset = 0;
|
|
mLastContentIsComplete = PR_TRUE;
|
|
mHasBullet = PR_FALSE;
|
|
mIsBlock = PR_FALSE;
|
|
mBounds.SetRect(0, 0, 0, 0);
|
|
}
|
|
|
|
nsLineData::~nsLineData()
|
|
{
|
|
}
|
|
|
|
void
|
|
nsLineData::UnlinkLine()
|
|
{
|
|
nsLineData* prevLine = mPrevLine;
|
|
nsLineData* nextLine = mNextLine;
|
|
if (nsnull != nextLine) nextLine->mPrevLine = prevLine;
|
|
if (nsnull != prevLine) prevLine->mNextLine = nextLine;
|
|
}
|
|
|
|
void
|
|
nsLineData::MoveLineBy(nscoord dx, nscoord dy)
|
|
{
|
|
nsIFrame* kid = mFirstChild;
|
|
nsPoint pt;
|
|
for (PRInt32 i = mChildCount; --i >= 0; ) {
|
|
kid->GetOrigin(pt);
|
|
pt.x += dx;
|
|
pt.y += dy;
|
|
kid->MoveTo(pt.x, pt.y);
|
|
kid->GetNextSibling(kid);
|
|
}
|
|
mBounds.MoveBy(dx, dy);
|
|
}
|
|
|
|
nsresult
|
|
nsLineData::Verify(PRBool aFinalCheck) const
|
|
{
|
|
NS_ASSERTION(mNextLine != this, "bad line linkage");
|
|
NS_ASSERTION(mPrevLine != this, "bad line linkage");
|
|
if (nsnull != mPrevLine) {
|
|
NS_ASSERTION(mPrevLine->mNextLine == this, "bad line linkage");
|
|
}
|
|
if (nsnull != mNextLine) {
|
|
NS_ASSERTION(mNextLine->mPrevLine == this, "bad line linkage");
|
|
}
|
|
|
|
if (aFinalCheck) {
|
|
NS_ASSERTION(0 != mChildCount, "empty line");
|
|
NS_ASSERTION(nsnull != mFirstChild, "empty line");
|
|
|
|
nsIFrame* nextLinesFirstChild = nsnull;
|
|
if (nsnull != mNextLine) {
|
|
nextLinesFirstChild = mNextLine->mFirstChild;
|
|
}
|
|
|
|
|
|
// Check that number of children are ok and that the index in parent
|
|
// information agrees with the content offsets.
|
|
PRInt32 offset = mFirstContentOffset;
|
|
PRInt32 len = 0;
|
|
nsIFrame* child = mFirstChild;
|
|
if (mHasBullet) {
|
|
// Skip bullet
|
|
child->GetNextSibling(child);
|
|
len++;
|
|
}
|
|
while ((nsnull != child) && (child != nextLinesFirstChild)) {
|
|
PRInt32 indexInParent;
|
|
child->GetContentIndex(indexInParent);
|
|
NS_ASSERTION(indexInParent == offset, "bad line offsets");
|
|
len++;
|
|
if (len != mChildCount) {
|
|
offset++;
|
|
}
|
|
child->GetNextSibling(child);
|
|
}
|
|
NS_ASSERTION(offset == mLastContentOffset, "bad mLastContentOffset");
|
|
NS_ASSERTION(len == mChildCount, "bad child count");
|
|
}
|
|
|
|
// XXX verify content offsets and mLastContentIsComplete
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIFrame*
|
|
nsLineData::GetLastChild()
|
|
{
|
|
nsIFrame* lastChild = mFirstChild;
|
|
if (mChildCount > 1) {
|
|
for (PRInt32 numKids = mChildCount - 1; --numKids >= 0; ) {
|
|
nsIFrame* nextChild;
|
|
lastChild->GetNextSibling(nextChild);
|
|
lastChild = nextChild;
|
|
}
|
|
}
|
|
return lastChild;
|
|
}
|
|
|
|
PRIntn
|
|
nsLineData::GetLineNumber() const
|
|
{
|
|
PRIntn lineNumber = 0;
|
|
nsLineData* prev = mPrevLine;
|
|
while (nsnull != prev) {
|
|
lineNumber++;
|
|
prev = prev->mPrevLine;
|
|
}
|
|
return lineNumber;
|
|
}
|
|
|
|
void
|
|
nsLineData::List(FILE* out, PRInt32 aIndent) const
|
|
{
|
|
// Indent
|
|
for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
|
|
// Output the first/last content offset
|
|
fprintf(out, "line %d [%d,%d,%c] ",
|
|
GetLineNumber(),
|
|
mFirstContentOffset, mLastContentOffset,
|
|
(mLastContentIsComplete ? 'T' : 'F'));
|
|
|
|
// Output the bounds rect
|
|
out << mBounds;
|
|
|
|
// Output the children, one line at a time
|
|
if (nsnull != mFirstChild) {
|
|
fputs("<\n", out);
|
|
aIndent++;
|
|
|
|
nsIFrame* child = mFirstChild;
|
|
for (PRInt32 numKids = mChildCount; --numKids >= 0; ) {
|
|
child->List(out, aIndent);
|
|
child->GetNextSibling(child);
|
|
}
|
|
|
|
aIndent--;
|
|
for (PRInt32 i = aIndent; --i >= 0; ) fputs(" ", out);
|
|
fputs(">\n", out);
|
|
} else {
|
|
fputs("<>\n", out);
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsLineLayout::nsLineLayout(nsBlockReflowState& aState)
|
|
{
|
|
mBlock = aState.mBlock;
|
|
mSpaceManager = aState.mSpaceManager;
|
|
mBlock->GetContent(mBlockContent);
|
|
mPresContext = aState.mPresContext;
|
|
#if 0
|
|
// XXX Do we still need this?
|
|
mBlockIsPseudo = aState.mBlockIsPseudo;
|
|
#endif
|
|
mUnconstrainedWidth = aState.mUnconstrainedWidth;
|
|
mUnconstrainedHeight = aState.mUnconstrainedHeight;
|
|
mMaxElementSizePointer = aState.mMaxElementSizePointer;
|
|
|
|
mAscents = mAscentBuf;
|
|
mMaxAscents = sizeof(mAscentBuf) / sizeof(mAscentBuf[0]);
|
|
}
|
|
|
|
nsLineLayout::~nsLineLayout()
|
|
{
|
|
NS_IF_RELEASE(mBlockContent);
|
|
if (mAscents != mAscentBuf) {
|
|
delete [] mAscents;
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsLineLayout::Initialize(nsBlockReflowState& aState, nsLineData* aLine)
|
|
{
|
|
nsresult rv = NS_OK;
|
|
|
|
mLine = aLine;
|
|
mKidPrevInFlow = nsnull;
|
|
mNewFrames = 0;
|
|
mKidIndex = aLine->mFirstContentOffset;
|
|
|
|
mReflowData.mMaxElementSize.width = 0;
|
|
mReflowData.mMaxElementSize.height = 0;
|
|
mReflowData.mMaxAscent = nsnull;
|
|
mReflowData.mMaxDescent = nsnull;
|
|
|
|
SetReflowSpace(aState.mCurrentBand.availSpace);
|
|
mY = aState.mY;
|
|
mMaxHeight = aState.mAvailSize.height;
|
|
mReflowDataChanged = PR_FALSE;
|
|
|
|
mLineHeight = 0;
|
|
mAscentNum = 0;
|
|
|
|
mKidFrame = nsnull;
|
|
mPrevKidFrame = nsnull;
|
|
|
|
mWordStart = nsnull;
|
|
mWordStartParent = nsnull;
|
|
mWordStartOffset = 0;
|
|
|
|
mSkipLeadingWhiteSpace = PR_TRUE;
|
|
mColumn = 0;
|
|
|
|
return rv;
|
|
}
|
|
|
|
void
|
|
nsLineLayout::SetReflowSpace(nsRect& aAvailableSpaceRect)
|
|
{
|
|
mReflowData.mX = aAvailableSpaceRect.x;
|
|
mReflowData.mAvailWidth = aAvailableSpaceRect.width;
|
|
mX0 = mReflowData.mX;
|
|
mMaxWidth = mReflowData.mAvailWidth;
|
|
mReflowDataChanged = PR_TRUE;
|
|
}
|
|
|
|
nsresult
|
|
nsLineLayout::AddAscent(nscoord aAscent)
|
|
{
|
|
if (mAscentNum == mMaxAscents) {
|
|
mMaxAscents *= 2;
|
|
nscoord* newAscents = new nscoord[mMaxAscents];
|
|
if (nsnull != newAscents) {
|
|
nsCRT::memcpy(newAscents, mAscents, sizeof(nscoord) * mAscentNum);
|
|
if (mAscents != mAscentBuf) {
|
|
delete [] mAscents;
|
|
}
|
|
mAscents = newAscents;
|
|
} else {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
mAscents[mAscentNum++] = aAscent;
|
|
return NS_OK;
|
|
}
|
|
|
|
nsIFrame*
|
|
nsLineLayout::GetWordStartParent()
|
|
{
|
|
if (nsnull == mWordStartParent) {
|
|
nsIFrame* frame = mWordStart;
|
|
for (;;) {
|
|
nsIFrame* parent;
|
|
frame->GetGeometricParent(parent);
|
|
if (nsnull == parent) {
|
|
break;
|
|
}
|
|
if (mBlock == parent) {
|
|
break;
|
|
}
|
|
frame = parent;
|
|
}
|
|
mWordStartParent = frame;
|
|
}
|
|
return mWordStartParent;
|
|
}
|
|
|
|
nsresult
|
|
nsLineLayout::WordBreakReflow()
|
|
{
|
|
// Restore line layout state to just before the word start.
|
|
mReflowData = mWordStartReflowData;
|
|
|
|
// Walk up from the frame that contains the start of the word to the
|
|
// child of the block that contains the word.
|
|
nsIFrame* frame = GetWordStartParent();
|
|
|
|
// Compute the available space to reflow the child. Note that since
|
|
// we are reflowing this child for the second time we know that the
|
|
// child will fit before we begin.
|
|
nsresult rv;
|
|
nsSize kidAvailSize;
|
|
kidAvailSize.width = mReflowData.mAvailWidth;
|
|
kidAvailSize.height = mMaxHeight;
|
|
if (!mUnconstrainedWidth) {
|
|
nsIStyleContextPtr kidSC;
|
|
rv = frame->GetStyleContext(mPresContext, kidSC.AssignRef());
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
nsStyleSpacing* kidSpacing = (nsStyleSpacing*)
|
|
kidSC->GetData(kStyleSpacingSID);
|
|
nsMargin kidMargin;
|
|
kidSpacing->CalcMarginFor(frame, kidMargin);
|
|
kidAvailSize.width -= kidMargin.left + kidMargin.right;
|
|
}
|
|
|
|
// Reflow that child of the block having set the reflow type so that
|
|
// the child knows whats going on.
|
|
mReflowType = NS_LINE_LAYOUT_REFLOW_TYPE_WORD_WRAP;
|
|
mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_NOT_AWARE;
|
|
nsSize maxElementSize;
|
|
nsReflowMetrics kidSize;
|
|
nsReflowStatus kidReflowStatus;
|
|
nsSize* kidMaxElementSize = nsnull;
|
|
if (nsnull != mMaxElementSizePointer) {
|
|
kidMaxElementSize = &maxElementSize;
|
|
}
|
|
rv = mBlock->ReflowInlineChild(frame, mPresContext, kidSize,
|
|
kidAvailSize, kidMaxElementSize,
|
|
kidReflowStatus);
|
|
|
|
return rv;
|
|
}
|
|
|
|
// Return values: <0 for error
|
|
// 0 == NS_LINE_LAYOUT
|
|
nsresult
|
|
nsLineLayout::ReflowChild(nsReflowCommand* aReflowCommand)
|
|
{
|
|
// Get kid frame's style context
|
|
nsIStyleContextPtr kidSC;
|
|
nsresult rv = mKidFrame->GetStyleContext(mPresContext, kidSC.AssignRef());
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
// See if frame belongs in the line.
|
|
// XXX absolute positioning
|
|
// XXX floating frames
|
|
// XXX break-before
|
|
nsStyleDisplay * kidDisplay = (nsStyleDisplay*)
|
|
kidSC->GetData(kStyleDisplaySID);
|
|
PRBool isBlock = PR_FALSE;
|
|
PRBool isFirstChild = PRBool(mKidFrame == mLine->mFirstChild);
|
|
switch (kidDisplay->mDisplay) {
|
|
case NS_STYLE_DISPLAY_NONE:
|
|
// Make sure the frame remains zero sized.
|
|
mKidFrame->SetRect(nsRect(mReflowData.mX, mY, 0, 0));
|
|
return NS_LINE_LAYOUT_COMPLETE;
|
|
case NS_STYLE_DISPLAY_INLINE:
|
|
break;
|
|
default:
|
|
isBlock = PR_TRUE;
|
|
if (!isFirstChild) {
|
|
return NS_LINE_LAYOUT_BREAK_BEFORE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Get the available size to reflow the child into
|
|
nsSize kidAvailSize;
|
|
kidAvailSize.width = mReflowData.mAvailWidth;
|
|
kidAvailSize.height = mMaxHeight;
|
|
nsStyleSpacing* kidSpacing = (nsStyleSpacing*)
|
|
kidSC->GetData(kStyleSpacingSID);
|
|
nsMargin kidMargin;
|
|
kidSpacing->CalcMarginFor(mKidFrame, kidMargin);
|
|
if (!mUnconstrainedWidth) {
|
|
kidAvailSize.width -= kidMargin.left + kidMargin.right;
|
|
if (!isFirstChild && (kidAvailSize.width <= 0)) {
|
|
// No room.
|
|
return NS_LINE_LAYOUT_BREAK_BEFORE;
|
|
}
|
|
}
|
|
|
|
// Reflow the child
|
|
nsRect kidRect;
|
|
nsSize maxElementSize;
|
|
nsReflowMetrics kidSize;
|
|
nsSize* kidMaxElementSize = nsnull;
|
|
nsReflowStatus kidReflowStatus;
|
|
if (nsnull != mMaxElementSizePointer) {
|
|
kidMaxElementSize = &maxElementSize;
|
|
}
|
|
mReflowResult = NS_LINE_LAYOUT_REFLOW_RESULT_NOT_AWARE;
|
|
nscoord dx = mReflowData.mX + kidMargin.left;
|
|
if (aReflowCommand) {
|
|
nsIFrame* nextFrame;
|
|
|
|
mSpaceManager->Translate(dx, mY);
|
|
kidReflowStatus = aReflowCommand->Next(mSpaceManager, kidRect, kidAvailSize, nextFrame);
|
|
mSpaceManager->Translate(-dx, -mY);
|
|
kidRect.x = dx;
|
|
kidRect.y = mY;
|
|
kidSize.width = kidRect.width;
|
|
kidSize.height = kidRect.height;
|
|
kidSize.ascent = kidRect.height;
|
|
kidSize.descent = 0;
|
|
|
|
} else if (isBlock) {
|
|
mSpaceManager->Translate(dx, mY);
|
|
rv = mBlock->ReflowBlockChild(mKidFrame, mPresContext,
|
|
mSpaceManager, kidAvailSize, kidRect,
|
|
kidMaxElementSize, kidReflowStatus);
|
|
mSpaceManager->Translate(-dx, -mY);
|
|
kidRect.x = dx;
|
|
kidRect.y = mY;
|
|
kidSize.width = kidRect.width;
|
|
kidSize.height = kidRect.height;
|
|
kidSize.ascent = kidRect.height;
|
|
kidSize.descent = 0;
|
|
}
|
|
else {
|
|
rv = mBlock->ReflowInlineChild(mKidFrame, mPresContext,
|
|
kidSize, kidAvailSize, kidMaxElementSize,
|
|
kidReflowStatus);
|
|
kidRect.x = dx;
|
|
kidRect.y = mY;
|
|
kidRect.width = kidSize.width;
|
|
kidRect.height = kidSize.height;
|
|
}
|
|
if (NS_OK != rv) return rv;
|
|
|
|
// See if the child fit
|
|
if (kidSize.width > kidAvailSize.width) {
|
|
// The child took up too much space. This condition is ignored if
|
|
// the child is the first child (by definition the first child
|
|
// always fits) or we have a word start and the word start is the
|
|
// first child.
|
|
if (!isFirstChild) {
|
|
// It's not our first child.
|
|
if (nsnull != mWordStart) {
|
|
// We have a word to break at
|
|
if (GetWordStartParent() != mLine->mFirstChild) {
|
|
// The word is not our first child
|
|
WordBreakReflow();
|
|
// XXX mKidPrevInFlow
|
|
return NS_LINE_LAYOUT_BREAK_BEFORE;
|
|
}
|
|
}
|
|
else {
|
|
// There is no word to break at and it's not our first child.
|
|
// We are out of room.
|
|
// XXX mKidPrevInFlow
|
|
return NS_LINE_LAYOUT_BREAK_BEFORE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// For non-aware children they act like words which means that space
|
|
// immediately following them must not be skipped over.
|
|
if (NS_LINE_LAYOUT_REFLOW_RESULT_NOT_AWARE == mReflowResult) {
|
|
mSkipLeadingWhiteSpace = PR_FALSE;
|
|
}
|
|
|
|
// Place child
|
|
mKidFrame->SetRect(kidRect);
|
|
|
|
// Advance
|
|
// XXX RTL
|
|
nscoord horizontalMargins = kidMargin.left +
|
|
kidMargin.right;
|
|
nscoord totalWidth = kidSize.width + horizontalMargins;
|
|
mReflowData.mX += totalWidth;
|
|
if (!mUnconstrainedWidth) {
|
|
mReflowData.mAvailWidth -= totalWidth;
|
|
}
|
|
if (nsnull != mMaxElementSizePointer) {
|
|
nscoord elementWidth = maxElementSize.width + horizontalMargins;
|
|
if (elementWidth > mReflowData.mMaxElementSize.width) {
|
|
mReflowData.mMaxElementSize.width = elementWidth;
|
|
}
|
|
if (kidSize.height > mReflowData.mMaxElementSize.height) {
|
|
mReflowData.mMaxElementSize.height = kidSize.height;
|
|
}
|
|
}
|
|
if (kidSize.ascent > mReflowData.mMaxAscent) {
|
|
mReflowData.mMaxAscent = kidSize.ascent;
|
|
}
|
|
if (kidSize.descent > mReflowData.mMaxDescent) {
|
|
mReflowData.mMaxDescent = kidSize.descent;
|
|
}
|
|
AddAscent(isBlock ? 0 : kidSize.ascent);
|
|
mLine->mIsBlock = isBlock;
|
|
|
|
// Set completion status
|
|
mLine->mLastContentOffset = mKidIndex;
|
|
if (NS_FRAME_IS_COMPLETE(kidReflowStatus)) {
|
|
mLine->mLastContentIsComplete = PR_TRUE;
|
|
if (isBlock ||
|
|
(NS_LINE_LAYOUT_REFLOW_RESULT_BREAK_AFTER == mReflowResult)) {
|
|
rv = NS_LINE_LAYOUT_BREAK_AFTER;
|
|
}
|
|
mKidPrevInFlow = nsnull;
|
|
}
|
|
else {
|
|
mLine->mLastContentIsComplete = PR_FALSE;
|
|
rv = NS_LINE_LAYOUT_NOT_COMPLETE;
|
|
mKidPrevInFlow = mKidFrame;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLineLayout::IncrementalReflowFromChild(nsReflowCommand& aReflowCommand,
|
|
nsIFrame* aChildFrame)
|
|
{
|
|
#if 0
|
|
// Get the current bounds. We'll need this to adjust the frames that follow
|
|
nsRect oldBounds;
|
|
aChildFrame->GetRect(oldBounds);
|
|
#endif
|
|
|
|
// For the time being reflow all the children, and when we get to aChildFrame
|
|
// handle it specially
|
|
nsresult reflowStatus = NS_LINE_LAYOUT_COMPLETE;
|
|
|
|
mLine->mBounds.x = mReflowData.mX;
|
|
mLine->mBounds.y = mY;
|
|
mKidFrame = mLine->mFirstChild;
|
|
PRInt32 kidNum = 0;
|
|
while (kidNum < mLine->mChildCount) {
|
|
// XXX Code to avoid reflowing a child goes here
|
|
nsresult childReflowStatus;
|
|
|
|
if (mKidFrame == aChildFrame) {
|
|
childReflowStatus = ReflowChild(&aReflowCommand);
|
|
} else {
|
|
childReflowStatus = ReflowChild(nsnull);
|
|
}
|
|
|
|
if (childReflowStatus < 0) {
|
|
reflowStatus = childReflowStatus;
|
|
goto done;
|
|
}
|
|
|
|
switch (childReflowStatus) {
|
|
default:
|
|
case NS_LINE_LAYOUT_COMPLETE:
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame->GetNextSibling(mKidFrame);
|
|
mKidIndex++;
|
|
kidNum++;
|
|
break;
|
|
|
|
case NS_LINE_LAYOUT_NOT_COMPLETE:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame->GetNextSibling(mKidFrame);
|
|
kidNum++;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_BEFORE:
|
|
reflowStatus = childReflowStatus;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_AFTER:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame->GetNextSibling(mKidFrame);
|
|
mKidIndex++;
|
|
kidNum++;
|
|
|
|
split_line:
|
|
reflowStatus = SplitLine(childReflowStatus, mLine->mChildCount - kidNum);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
// Perform alignment operations
|
|
if (mLine->mIsBlock) {
|
|
mLineHeight = mReflowData.mMaxAscent + mReflowData.mMaxDescent;
|
|
}
|
|
else {
|
|
AlignChildren();
|
|
}
|
|
|
|
// Set final bounds of the line
|
|
mLine->mBounds.height = mLineHeight;
|
|
mLine->mBounds.width = mReflowData.mX - mLine->mBounds.x;
|
|
|
|
NS_ASSERTION(((reflowStatus < 0) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_NOT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_BEFORE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_AFTER)),
|
|
"bad return status from ReflowMapped");
|
|
return reflowStatus;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsLineLayout::SplitLine(PRInt32 aChildReflowStatus, PRInt32 aRemainingKids)
|
|
{
|
|
nsresult rv = NS_LINE_LAYOUT_COMPLETE;
|
|
|
|
if (NS_LINE_LAYOUT_NOT_COMPLETE == aChildReflowStatus) {
|
|
// When a line is not complete it indicates that the last child on
|
|
// the line reflowed and took some space but wasn't given enough
|
|
// space to complete. Sometimes when this happens we will need to
|
|
// create a next-in-flow for the child.
|
|
nsIFrame* nextInFlow;
|
|
mPrevKidFrame->GetNextInFlow(nextInFlow);
|
|
if (nsnull == nextInFlow) {
|
|
// Create a continuation frame for the child frame and insert it
|
|
// into our lines child list.
|
|
nsIFrame* nextFrame;
|
|
mPrevKidFrame->GetNextSibling(nextFrame);
|
|
nsIStyleContext* kidSC;
|
|
mPrevKidFrame->GetStyleContext(mPresContext, kidSC);
|
|
mPrevKidFrame->CreateContinuingFrame(mPresContext, mBlock, kidSC,
|
|
nextInFlow);
|
|
NS_RELEASE(kidSC);
|
|
if (nsnull == nextInFlow) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
mPrevKidFrame->SetNextSibling(nextInFlow);
|
|
nextInFlow->SetNextSibling(nextFrame);
|
|
mNewFrames++;
|
|
|
|
// Add new child to our line
|
|
mLine->mChildCount++;
|
|
|
|
// Set mKidFrame to the new next-in-flow so that we will
|
|
// push it when we push children. Increment the number of
|
|
// remaining kids now that there is one more.
|
|
mKidFrame = nextInFlow;
|
|
aRemainingKids++;
|
|
}
|
|
}
|
|
|
|
if (0 != aRemainingKids) {
|
|
NS_ASSERTION(nsnull != mKidFrame, "whoops");
|
|
nsLineData* from = mLine;
|
|
nsLineData* to = mLine->mNextLine;
|
|
if (nsnull != to) {
|
|
// Only push into the next line if it's empty; otherwise we can
|
|
// end up pushing a frame which is continued into the same frame
|
|
// as it's continuation. This causes all sorts of side effects
|
|
// so we don't allow it.
|
|
if (to->mChildCount != 0) {
|
|
nsLineData* insertedLine = new nsLineData();
|
|
from->mNextLine = insertedLine;
|
|
to->mPrevLine = insertedLine;
|
|
insertedLine->mPrevLine = from;
|
|
insertedLine->mNextLine = to;
|
|
to = insertedLine;
|
|
to->mLastContentOffset = from->mLastContentOffset;
|
|
to->mLastContentIsComplete = from->mLastContentIsComplete;
|
|
}
|
|
} else {
|
|
to = new nsLineData();
|
|
to->mPrevLine = from;
|
|
from->mNextLine = to;
|
|
}
|
|
if (nsnull == to) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
PRInt32 kidIndexInParent;
|
|
mKidFrame->GetContentIndex(kidIndexInParent);
|
|
to->mFirstChild = mKidFrame;
|
|
to->mChildCount += aRemainingKids;
|
|
to->mFirstContentOffset = kidIndexInParent;
|
|
|
|
// The to-line is going to be reflowed therefore it's last content
|
|
// offset and completion status don't matter. In fact, it's expensive
|
|
// to compute them so don't bother.
|
|
#ifdef NS_DEBUG
|
|
to->mLastContentOffset = -1;
|
|
to->mLastContentIsComplete = PRPackedBool(0x255);
|
|
#endif
|
|
|
|
from->mChildCount -= aRemainingKids;
|
|
NS_ASSERTION(0 != from->mChildCount, "bad push");
|
|
#ifdef NS_DEBUG
|
|
from->Verify();
|
|
#endif
|
|
#ifdef NOISY_REFLOW
|
|
printf("After push, from-line (%d):\n", aRemainingKids);
|
|
from->List(stdout, 1);
|
|
printf("After push, to-line:\n");
|
|
to->List(stdout, 1);
|
|
#endif
|
|
}
|
|
|
|
return aChildReflowStatus;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsLineLayout::ReflowMapped()
|
|
{
|
|
nsresult reflowStatus = NS_LINE_LAYOUT_COMPLETE;
|
|
|
|
mKidFrame = mLine->mFirstChild;
|
|
PRInt32 kidNum = 0;
|
|
while (kidNum < mLine->mChildCount) {
|
|
// XXX Code to avoid reflowing a child goes here
|
|
|
|
nsresult childReflowStatus = ReflowChild(nsnull);
|
|
if (childReflowStatus < 0) {
|
|
reflowStatus = childReflowStatus;
|
|
goto done;
|
|
}
|
|
switch (childReflowStatus) {
|
|
default:
|
|
case NS_LINE_LAYOUT_COMPLETE:
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame->GetNextSibling(mKidFrame);
|
|
mKidIndex++;
|
|
kidNum++;
|
|
break;
|
|
|
|
case NS_LINE_LAYOUT_NOT_COMPLETE:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame->GetNextSibling(mKidFrame);
|
|
kidNum++;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_BEFORE:
|
|
reflowStatus = childReflowStatus;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_AFTER:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame->GetNextSibling(mKidFrame);
|
|
mKidIndex++;
|
|
kidNum++;
|
|
|
|
split_line:
|
|
reflowStatus = SplitLine(childReflowStatus, mLine->mChildCount - kidNum);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
done:
|
|
NS_ASSERTION(((reflowStatus < 0) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_NOT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_BEFORE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_AFTER)),
|
|
"bad return status from ReflowMapped");
|
|
return reflowStatus;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
static PRBool
|
|
IsBlock(nsStyleDisplay* aDisplay)
|
|
{
|
|
switch (aDisplay->mDisplay) {
|
|
case NS_STYLE_DISPLAY_BLOCK:
|
|
case NS_STYLE_DISPLAY_LIST_ITEM:
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// XXX fix this code to look at the available width and if it's too
|
|
// small for the next child then skip the pullup (and return
|
|
// BREAK_AFTER status).
|
|
|
|
nsresult
|
|
nsLineLayout::PullUpChildren()
|
|
{
|
|
nsresult reflowStatus = NS_LINE_LAYOUT_COMPLETE;
|
|
nsIFrame* prevKidFrame = mPrevKidFrame;
|
|
|
|
nsBlockFrame* currentBlock = mBlock;
|
|
nsLineData* line = mLine->mNextLine;
|
|
while (nsnull != currentBlock) {
|
|
|
|
// Pull children from the next line
|
|
while (nsnull != line) {
|
|
// Get first child from next line
|
|
mKidFrame = line->mFirstChild;
|
|
if (nsnull == mKidFrame) {
|
|
NS_ASSERTION(0 == line->mChildCount, "bad line list");
|
|
nsLineData* nextLine = line->mNextLine;
|
|
nsLineData* prevLine = line->mPrevLine;
|
|
if (nsnull != prevLine) prevLine->mNextLine = nextLine;
|
|
if (nsnull != nextLine) nextLine->mPrevLine = prevLine;
|
|
delete line;/* XXX free-list in block-reflow-state? */
|
|
line = nextLine;
|
|
continue;
|
|
}
|
|
|
|
// XXX Avoid the pullup work if the child cannot already fit
|
|
// (e.g. it's not splittable and can't fit)
|
|
|
|
// If the child is a block element then if this is not the first
|
|
// line in the block or if it's the first line and it's not the
|
|
// first child in the line then we cannot pull-up the child.
|
|
nsresult rv;
|
|
nsIStyleContextPtr kidSC;
|
|
rv = mKidFrame->GetStyleContext(mPresContext, kidSC.AssignRef());
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
|
|
kidSC->GetData(kStyleDisplaySID);
|
|
if (IsBlock(kidDisplay)) {
|
|
if ((nsnull != mLine->mPrevLine) || (0 != mLine->mChildCount)) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// Make pulled child part of this line
|
|
#ifdef NOISY_REFLOW
|
|
printf("Before Pullup:\n");
|
|
line->List(stdout, 1);
|
|
#endif
|
|
mLine->mChildCount++;
|
|
if (0 == --line->mChildCount) {
|
|
// Remove empty lines from the list
|
|
nsLineData* nextLine = line->mNextLine;
|
|
nsLineData* prevLine = line->mPrevLine;
|
|
if (nsnull != prevLine) prevLine->mNextLine = nextLine;
|
|
if (nsnull != nextLine) nextLine->mPrevLine = prevLine;
|
|
delete line;/* XXX free-list in block-reflow-state? */
|
|
line = nextLine;
|
|
}
|
|
else {
|
|
// Repair the first content offset of the line. The first
|
|
// child of the line's index-in-parent should be the line's
|
|
// new first content offset.
|
|
mKidFrame->GetNextSibling(line->mFirstChild);
|
|
PRInt32 indexInParent;
|
|
line->mFirstChild->GetContentIndex(indexInParent);
|
|
line->mFirstContentOffset = indexInParent;
|
|
#ifdef NS_DEBUG
|
|
line->Verify();
|
|
#endif
|
|
}
|
|
|
|
// Try to reflow it like any other mapped child
|
|
nsresult childReflowStatus = ReflowChild(nsnull);
|
|
if (childReflowStatus < 0) {
|
|
reflowStatus = childReflowStatus;
|
|
goto done;
|
|
}
|
|
PRInt32 pushCount;
|
|
switch (childReflowStatus) {
|
|
default:
|
|
case NS_LINE_LAYOUT_COMPLETE:
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame = nsnull;
|
|
mKidIndex++;
|
|
break;
|
|
|
|
case NS_LINE_LAYOUT_NOT_COMPLETE:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame = nsnull;
|
|
pushCount = 0;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_BEFORE:
|
|
reflowStatus = childReflowStatus;
|
|
pushCount = 1;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_AFTER:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame = nsnull;
|
|
mKidIndex++;
|
|
pushCount = 0;
|
|
|
|
split_line:
|
|
reflowStatus = SplitLine(childReflowStatus, pushCount);
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
// Grab the block's next in flow
|
|
nsIFrame* nextInFlow;
|
|
currentBlock->GetNextInFlow(nextInFlow);
|
|
currentBlock = (nsBlockFrame*)nextInFlow;
|
|
if (nsnull != currentBlock) {
|
|
line = currentBlock->GetFirstLine();
|
|
}
|
|
}
|
|
|
|
done:
|
|
NS_ASSERTION(((reflowStatus < 0) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_NOT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_BEFORE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_AFTER)),
|
|
"bad return status from PullUpChildren");
|
|
return reflowStatus;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsLineLayout::CreateFrameFor(nsIContent* aKid)
|
|
{
|
|
nsIStyleContextPtr kidSC =
|
|
mPresContext->ResolveStyleContextFor(aKid, mBlock); // XXX bad API
|
|
if (nsnull == kidSC) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
nsStylePosition* kidPosition = (nsStylePosition*)
|
|
kidSC->GetData(kStylePositionSID);
|
|
nsStyleDisplay* kidDisplay = (nsStyleDisplay*)
|
|
kidSC->GetData(kStyleDisplaySID);
|
|
|
|
// Check whether it wants to floated or absolutely positioned
|
|
PRBool isBlock = PR_FALSE;
|
|
nsIFrame* kidFrame;
|
|
nsresult rv;
|
|
if (NS_STYLE_POSITION_ABSOLUTE == kidPosition->mPosition) {
|
|
rv = AbsoluteFrame::NewFrame(&kidFrame, aKid, mBlock);
|
|
if (NS_OK == rv) {
|
|
kidFrame->SetStyleContext(mPresContext, kidSC);
|
|
}
|
|
} else if (kidDisplay->mFloats != NS_STYLE_FLOAT_NONE) {
|
|
rv = PlaceholderFrame::NewFrame(&kidFrame, aKid, mBlock);
|
|
if (NS_OK == rv) {
|
|
kidFrame->SetStyleContext(mPresContext, kidSC);
|
|
}
|
|
} else if (nsnull == mKidPrevInFlow) {
|
|
// Create initial frame for the child
|
|
nsIContentDelegate* kidDel;
|
|
switch (kidDisplay->mDisplay) {
|
|
case NS_STYLE_DISPLAY_NONE:
|
|
rv = nsFrame::NewFrame(&kidFrame, aKid, mBlock);
|
|
if (NS_OK == rv) {
|
|
kidFrame->SetStyleContext(mPresContext, kidSC);
|
|
}
|
|
break;
|
|
|
|
case NS_STYLE_DISPLAY_BLOCK:
|
|
case NS_STYLE_DISPLAY_LIST_ITEM:
|
|
isBlock = PR_TRUE;
|
|
// FALL THROUGH
|
|
default:
|
|
kidDel = aKid->GetDelegate(mPresContext);
|
|
rv = kidDel->CreateFrame(mPresContext, aKid, mBlock, kidSC, kidFrame);
|
|
NS_RELEASE(kidDel);
|
|
break;
|
|
}
|
|
} else {
|
|
// Since kid has a prev-in-flow, use that to create the next
|
|
// frame.
|
|
rv = mKidPrevInFlow->CreateContinuingFrame(mPresContext, mBlock, kidSC,
|
|
kidFrame);
|
|
NS_ASSERTION(0 == mLine->mChildCount, "bad continuation");
|
|
}
|
|
if (NS_OK != rv) {
|
|
return rv;
|
|
}
|
|
|
|
mKidFrame = kidFrame;
|
|
mNewFrames++;
|
|
|
|
if (isBlock && (0 != mLine->mChildCount)) {
|
|
// When we are not at the start of a line we need to break
|
|
// before a block element.
|
|
return NS_LINE_LAYOUT_BREAK_BEFORE;
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLineLayout::ReflowUnmapped()
|
|
{
|
|
nsresult reflowStatus = NS_LINE_LAYOUT_COMPLETE;
|
|
for (;;) {
|
|
nsIContentPtr kid = mBlockContent->ChildAt(mKidIndex);
|
|
if (kid.IsNull()) {
|
|
break;
|
|
}
|
|
|
|
// Create a frame for the new content
|
|
nsresult rv = CreateFrameFor(kid);
|
|
if (rv < 0) {
|
|
reflowStatus = rv;
|
|
goto done;
|
|
}
|
|
if (NS_LINE_LAYOUT_PSEUDO_BREAK_BEFORE_BLOCK == rv) {
|
|
// We have found a child that should not be a part of the
|
|
// block. Therefore we are finished!
|
|
goto done;
|
|
}
|
|
|
|
// Add frame to our list
|
|
if (nsnull != mPrevKidFrame) {
|
|
mPrevKidFrame->SetNextSibling(mKidFrame);
|
|
}
|
|
if (0 == mLine->mChildCount) {
|
|
mLine->mFirstChild = mKidFrame;
|
|
}
|
|
mLine->mChildCount++;
|
|
|
|
nsresult childReflowStatus;
|
|
PRInt32 pushCount;
|
|
if (rv == NS_LINE_LAYOUT_BREAK_BEFORE) {
|
|
// If we break before a frame is even supposed to layout then we
|
|
// need to split the line.
|
|
childReflowStatus = rv;
|
|
pushCount = 1;
|
|
goto split_line;
|
|
}
|
|
|
|
// Reflow new child frame
|
|
childReflowStatus = ReflowChild(nsnull);
|
|
if (childReflowStatus < 0) {
|
|
reflowStatus = childReflowStatus;
|
|
goto done;
|
|
}
|
|
switch (childReflowStatus) {
|
|
default:
|
|
case NS_LINE_LAYOUT_COMPLETE:
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame = nsnull;
|
|
mKidIndex++;
|
|
break;
|
|
|
|
case NS_LINE_LAYOUT_NOT_COMPLETE:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame = nsnull;
|
|
pushCount = 0;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_BEFORE:
|
|
reflowStatus = childReflowStatus;
|
|
pushCount = 1;
|
|
goto split_line;
|
|
|
|
case NS_LINE_LAYOUT_BREAK_AFTER:
|
|
reflowStatus = childReflowStatus;
|
|
mPrevKidFrame = mKidFrame;
|
|
mKidFrame = nsnull;
|
|
mKidIndex++;
|
|
pushCount = 0;
|
|
|
|
split_line:
|
|
reflowStatus = SplitLine(childReflowStatus, pushCount);
|
|
goto done;
|
|
}
|
|
}
|
|
NS_ASSERTION(nsnull == mLine->mNextLine, "bad line list");
|
|
|
|
done:
|
|
NS_ASSERTION(((reflowStatus < 0) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_NOT_COMPLETE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_BEFORE) ||
|
|
(reflowStatus == NS_LINE_LAYOUT_BREAK_AFTER)),
|
|
"bad return status from ReflowUnmapped");
|
|
return reflowStatus;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
nsresult
|
|
nsLineLayout::ReflowLine()
|
|
{
|
|
#ifdef NOISY_REFLOW
|
|
printf("Before ReflowLine:\n");
|
|
mLine->List(stdout, 1);
|
|
#endif
|
|
|
|
nsresult rv = NS_LINE_LAYOUT_COMPLETE;
|
|
|
|
mLine->mBounds.x = mReflowData.mX;
|
|
mLine->mBounds.y = mY;
|
|
|
|
// Reflow the mapped frames
|
|
if (0 != mLine->mChildCount) {
|
|
rv = ReflowMapped();
|
|
if (rv < 0) return rv;
|
|
}
|
|
|
|
// Pull-up any frames from the next line
|
|
if (NS_LINE_LAYOUT_COMPLETE == rv) {
|
|
if (nsnull != mLine->mNextLine) {
|
|
rv = PullUpChildren();
|
|
if (rv < 0) return rv;
|
|
}
|
|
|
|
// Try reflowing any unmapped children
|
|
if (NS_LINE_LAYOUT_COMPLETE == rv) {
|
|
if (nsnull == mLine->mNextLine) {
|
|
rv = ReflowUnmapped();
|
|
if (rv < 0) return rv;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Perform alignment operations
|
|
if (mLine->mIsBlock) {
|
|
mLineHeight = mReflowData.mMaxAscent + mReflowData.mMaxDescent;
|
|
}
|
|
else {
|
|
AlignChildren();
|
|
}
|
|
|
|
// Set final bounds of the line
|
|
mLine->mBounds.height = mLineHeight;
|
|
mLine->mBounds.width = mReflowData.mX - mLine->mBounds.x;
|
|
|
|
#ifdef NOISY_REFLOW
|
|
printf("After ReflowLine:\n");
|
|
mLine->List(stdout, 1);
|
|
#endif
|
|
#ifdef NS_DEBUG
|
|
mLine->Verify();
|
|
#endif
|
|
return rv;
|
|
}
|
|
|
|
nsresult
|
|
nsLineLayout::AlignChildren()
|
|
{
|
|
NS_PRECONDITION(mLine->mChildCount == mAscentNum, "bad line reflow");
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
nsIStyleContextPtr blockSC;
|
|
mBlock->GetStyleContext(mPresContext, blockSC.AssignRef());
|
|
nsStyleFont* blockFont = (nsStyleFont*)
|
|
blockSC->GetData(kStyleFontSID);
|
|
nsStyleText* blockText = (nsStyleText*)
|
|
blockSC->GetData(kStyleTextSID);
|
|
|
|
// First vertically align the children on the line; this will
|
|
// compute the actual line height for us.
|
|
mLineHeight =
|
|
nsCSSLayout::VerticallyAlignChildren(mPresContext, mBlock, blockFont,
|
|
mY,
|
|
mLine->mFirstChild,
|
|
mLine->mChildCount,
|
|
mAscents, mReflowData.mMaxAscent);
|
|
|
|
// Now horizontally place the children
|
|
nsCSSLayout::HorizontallyPlaceChildren(mPresContext, mBlock, blockText,
|
|
mLine->mFirstChild,
|
|
mLine->mChildCount,
|
|
mReflowData.mX - mX0,
|
|
mMaxWidth);
|
|
|
|
// Last, apply relative positioning
|
|
nsCSSLayout::RelativePositionChildren(mPresContext, mBlock,
|
|
mLine->mFirstChild,
|
|
mLine->mChildCount);
|
|
|
|
return rv;
|
|
}
|