Bug 379349. Add support for 'overflow containers' --- special frame continuations that do not map any content of their own, but serve only as containers for laying out children that overflowed their parents' content height. patch by fantasai, r+sr=eli,roc

This commit is contained in:
roc+@cs.cmu.edu 2007-07-25 21:03:29 -07:00
Родитель a883fd0427
Коммит 1da360b2d7
22 изменённых файлов: 1157 добавлений и 211 удалений

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

@ -320,6 +320,7 @@ GK_ATOM(error, "error")
GK_ATOM(even, "even")
GK_ATOM(event, "event")
GK_ATOM(events, "events")
GK_ATOM(excessOverflowContainersList, "ExcessOverflowContainers-list")
GK_ATOM(excludeResultPrefixes, "exclude-result-prefixes")
GK_ATOM(excludes, "excludes")
GK_ATOM(expr, "expr")
@ -624,6 +625,7 @@ GK_ATOM(output, "output")
GK_ATOM(overflow, "overflow")
GK_ATOM(overflowList, "Overflow-list")
GK_ATOM(overflowchanged, "overflowchanged")
GK_ATOM(overflowContainersList, "OverflowContainers-list")
GK_ATOM(overflowOutOfFlowList, "OverflowOutOfFlow-list")
GK_ATOM(overlay, "overlay")
GK_ATOM(overlap, "overlap")
@ -1461,6 +1463,8 @@ GK_ATOM(maxElementWidthProperty, "MaxElementWidthProperty") // nscoord*
GK_ATOM(outOfFlowDirtyRectProperty, "OutOfFlowDirtyRectProperty") // nsRect*
GK_ATOM(overflowAreaProperty, "OverflowArea") // nsRect*
GK_ATOM(overflowProperty, "OverflowProperty") // list of nsIFrame*
GK_ATOM(overflowContainersProperty, "OverflowContainersProperty") // nsFrameList*
GK_ATOM(excessOverflowContainersProperty, "ExcessOverflowContainersProperty") // nsFrameList*
GK_ATOM(overflowLinesProperty, "OverflowLinesProperty") // list of nsLineBox*
GK_ATOM(overflowOutOfFlowsProperty, "OverflowOutOfFlowsProperty") // nsFrameList*
GK_ATOM(overflowPlaceholdersProperty, "OverflowPlaceholdersProperty") // nsFrameList*

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

@ -4646,7 +4646,12 @@ nsCSSFrameConstructor::ConstructPageFrame(nsIPresShell* aPresShell,
// Initialize the page content frame and force it to have a view. Also make it the
// containing block for fixed elements which are repeated on every page.
aPageContentFrame->Init(nsnull, aPageFrame, nsnull);
nsIFrame* prevPageContentFrame = nsnull;
if (aPrevPageFrame) {
prevPageContentFrame = aPrevPageFrame->GetFirstChild(nsnull);
NS_ASSERTION(prevPageContentFrame, "missing page content frame");
}
aPageContentFrame->Init(nsnull, aPageFrame, prevPageContentFrame);
mFixedContainingBlock = aPageContentFrame;
aPageFrame->SetInitialChildList(nsnull, aPageContentFrame);

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

@ -1261,6 +1261,8 @@ nsComboboxControlFrame::SetInitialChildList(nsIAtom* aListName,
return rv;
}
#define NS_COMBO_FRAME_POPUP_LIST_INDEX (NS_BLOCK_LIST_COUNT)
nsIAtom*
nsComboboxControlFrame::GetAdditionalChildListName(PRInt32 aIndex) const
{
@ -1268,7 +1270,7 @@ nsComboboxControlFrame::GetAdditionalChildListName(PRInt32 aIndex) const
// This is necessary because we don't want the listbox to be included in the layout
// of the combox's children because it would take up space, when it is suppose to
// be floating above the display.
if (aIndex <= NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX) {
if (aIndex < NS_BLOCK_LIST_COUNT) {
return nsAreaFrame::GetAdditionalChildListName(aIndex);
}

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

@ -78,7 +78,7 @@ class nsComboboxDisplayFrame;
* Child list name indices
* @see #GetAdditionalChildListName()
*/
#define NS_COMBO_FRAME_POPUP_LIST_INDEX (NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX + 1)
#define NS_COMBO_LIST_COUNT (NS_BLOCK_LIST_COUNT + 1)
class nsComboboxControlFrame : public nsAreaFrame,
public nsIFormControlFrame,

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

@ -536,23 +536,28 @@ nsBlockFrame::GetFirstChild(nsIAtom* aListName) const
return mFloats.FirstChild();
}
else if (aListName == nsGkAtoms::bulletList) {
if (HaveOutsideBullet()) {
return mBullet;
}
return (HaveOutsideBullet()) ? mBullet : nsnull;
}
return nsnull;
return nsContainerFrame::GetFirstChild(aListName);;
}
#define NS_BLOCK_FRAME_OVERFLOW_OOF_LIST_INDEX (NS_CONTAINER_LIST_COUNT_INCL_OC + 0)
#define NS_BLOCK_FRAME_FLOAT_LIST_INDEX (NS_CONTAINER_LIST_COUNT_INCL_OC + 1)
#define NS_BLOCK_FRAME_BULLET_LIST_INDEX (NS_CONTAINER_LIST_COUNT_INCL_OC + 2)
#define NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX (NS_CONTAINER_LIST_COUNT_INCL_OC + 3)
// If adding/removing lists, don't forget to update the count in nsBlockFrame.h
nsIAtom*
nsBlockFrame::GetAdditionalChildListName(PRInt32 aIndex) const
{
if (aIndex < NS_CONTAINER_LIST_COUNT_INCL_OC)
return nsContainerFrame::GetAdditionalChildListName(aIndex);
switch (aIndex) {
case NS_BLOCK_FRAME_FLOAT_LIST_INDEX:
return nsGkAtoms::floatList;
case NS_BLOCK_FRAME_BULLET_LIST_INDEX:
return nsGkAtoms::bulletList;
case NS_BLOCK_FRAME_OVERFLOW_LIST_INDEX:
return nsGkAtoms::overflowList;
case NS_BLOCK_FRAME_OVERFLOW_OOF_LIST_INDEX:
return nsGkAtoms::overflowOutOfFlowList;
case NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX:
@ -830,7 +835,7 @@ CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState,
}
NS_IMETHODIMP
nsBlockFrame::Reflow(nsPresContext* aPresContext,
nsBlockFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aMetrics,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
@ -919,6 +924,14 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed");
if (NS_FAILED(rv)) return rv;
// Handle paginated overflow (see nsContainerFrame.h)
nsRect overflowContainerBounds;
if (GetPrevInFlow()) {
ReflowOverflowContainerChildren(aPresContext, aReflowState,
overflowContainerBounds, 0,
state.mReflowStatus);
}
// If the block is complete, put continuted floats in the closest ancestor
// block that uses the same space manager and leave the block complete; this
// allows subsequent lines on the page to be impacted by floats. If the
@ -927,7 +940,7 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
if (state.mOverflowPlaceholders.NotEmpty()) {
NS_ASSERTION(aReflowState.availableHeight != NS_UNCONSTRAINEDSIZE,
"Somehow we failed to fit all content, even though we have unlimited space!");
if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) {
if (NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) {
// find the nearest block ancestor that uses the same space manager
for (const nsHTMLReflowState* ancestorRS = aReflowState.parentReflowState;
ancestorRS;
@ -1006,7 +1019,8 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
}
state.mOverflowPlaceholders.SetFrames(nsnull);
}
state.mReflowStatus |= NS_FRAME_NOT_COMPLETE | NS_FRAME_REFLOW_NEXTINFLOW;
state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
NS_FRAME_SET_INCOMPLETE(state.mReflowStatus);
}
}
@ -1065,6 +1079,9 @@ nsBlockFrame::Reflow(nsPresContext* aPresContext,
ComputeFinalSize(aReflowState, state, aMetrics);
ComputeCombinedArea(aReflowState, aMetrics);
// Factor overflow container child bounds into the overflow area
aMetrics.mOverflowArea.UnionRect(aMetrics.mOverflowArea,
overflowContainerBounds);
// see if verifyReflow is enabled, and if so store off the space manager pointer
#ifdef DEBUG
@ -1261,9 +1278,19 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
// We may have stretched the frame beyond its computed height. Oh well.
computedHeightLeftOver = PR_MAX(0, computedHeightLeftOver);
}
NS_ASSERTION(!( (mState & NS_FRAME_IS_OVERFLOW_CONTAINER)
&& computedHeightLeftOver ),
"overflow container must not have computedHeightLeftOver");
aMetrics.height = borderPadding.top + computedHeightLeftOver + borderPadding.bottom;
if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)
&& aMetrics.height < aReflowState.availableHeight) {
// We ran out of height on this page but we're incomplete
// Set status to complete except for overflow
NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus);
}
if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) {
aMetrics.height = borderPadding.top + computedHeightLeftOver + borderPadding.bottom;
if (computedHeightLeftOver > 0 &&
aMetrics.height > aReflowState.availableHeight) {
// We don't fit and we consumed some of the computed height,
@ -1273,7 +1300,9 @@ nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState,
// border/padding to the next page/column.
aMetrics.height = PR_MAX(aReflowState.availableHeight,
aState.mY + nonCarriedOutVerticalMargin);
aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
if (!GetNextInFlow())
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
}
else {
@ -1860,6 +1889,11 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// used.
deltaY = line->mBounds.YMost() - oldYMost;
} else {
aState.mOverflowTracker.Skip(line->mFirstChild, aState.mReflowStatus);
// Nop except for blocks (we don't create overflow container
// continuations for any inlines atm), so only checking mFirstChild
// is enough
lastLineMovedUp = deltaY < 0;
if (deltaY != 0)
@ -1926,15 +1960,14 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
// didn't change)
// XXXldb We should also check that the first line of the next-in-flow
// isn't dirty.
if (!aState.mNextInFlow ||
if (aState.mNextInFlow &&
(aState.mReflowState.mFlags.mNextInFlowUntouched &&
!lastLineMovedUp &&
!(GetStateBits() & NS_FRAME_IS_DIRTY) &&
!reflowedFloat)) {
if (aState.mNextInFlow) {
aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
}
} else {
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
}
else if (aState.mNextInFlow) {
// Pull data from a next-in-flow if there's still room for more
// content here.
while (keepGoing && (nsnull != aState.mNextInFlow)) {
@ -2041,7 +2074,7 @@ nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState)
if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) {
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
} //XXXfr shouldn't set this flag when nextinflow has no lines
}
// Handle an odd-ball case: a list-item with no lines
@ -2926,7 +2959,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
UndoSplitPlaceholders(aState, lastPlaceholder);
PushLines(aState, aLine.prev());
*aKeepReflowGoing = PR_FALSE;
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
}
else {
// Note: line-break-after a block is a nop
@ -2962,65 +2995,113 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// Continue the block frame now if it didn't completely fit in
// the available space.
if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) {
PRBool madeContinuation;
rv = CreateContinuationFor(aState, nsnull, frame, madeContinuation);
NS_ENSURE_SUCCESS(rv, rv);
nsIFrame* nextFrame = frame->GetNextInFlow();
NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now");
// Push continuation to a new line, but only if we actually made one.
if (madeContinuation) {
nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE);
NS_ENSURE_TRUE(line, NS_ERROR_OUT_OF_MEMORY);
mLines.after_insert(aLine, line);
}
// Advance to next line since some of the block fit. That way
// only the following lines will be pushed.
PushLines(aState, aLine);
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
// If we need to reflow the continuation of the block child,
// then we'd better reflow our continuation
if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
// We also need to make that continuation's line dirty so
// it gets reflowed when we reflow our next in flow. The
// nif's line must always be either a line of the nif's
// parent block (only if we didn't make a continuation) or
// else one of our own overflow lines. In the latter case
// the line is already marked dirty, so just handle the
// first case.
if (!madeContinuation) {
nsBlockFrame* nifBlock = static_cast<nsBlockFrame*>(nextFrame->GetParent());
NS_ASSERTION(nifBlock->GetType() == nsGkAtoms::blockFrame
|| nifBlock->GetType() == nsGkAtoms::areaFrame,
"A block's child's next in flow's parent must be a block!");
for (line_iterator line = nifBlock->begin_lines(),
if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) {
// If nextFrame used to be an overflow container, make it a normal block
if (!madeContinuation &&
(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
aState.mOverflowTracker.Finish(frame);
nsContainerFrame* parent =
static_cast<nsContainerFrame*>(nextFrame->GetParent());
rv = parent->StealFrame(aState.mPresContext, nextFrame);
NS_ENSURE_SUCCESS(rv, rv);
ReparentFrame(nextFrame, parent, this);
nextFrame->SetNextSibling(frame->GetNextSibling());
frame->SetNextSibling(nextFrame);
madeContinuation = PR_TRUE; // needs to be added to mLines
nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
frameReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
// Push continuation to a new line, but only if we actually made one.
if (madeContinuation) {
nsLineBox* line = aState.NewLineBox(nextFrame, 1, PR_TRUE);
NS_ENSURE_TRUE(line, NS_ERROR_OUT_OF_MEMORY);
mLines.after_insert(aLine, line);
}
PushLines(aState, aLine);
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
// If we need to reflow the continuation of the block child,
// then we'd better reflow our continuation
if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) {
aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
// We also need to make that continuation's line dirty so
// it gets reflowed when we reflow our next in flow. The
// nif's line must always be either a line of the nif's
// parent block (only if we didn't make a continuation) or
// else one of our own overflow lines. In the latter case
// the line is already marked dirty, so just handle the
// first case.
if (!madeContinuation) {
nsBlockFrame* nifBlock = static_cast<nsBlockFrame*>(nextFrame->GetParent());
NS_ASSERTION(nifBlock->GetType() == nsGkAtoms::blockFrame
|| nifBlock->GetType() == nsGkAtoms::areaFrame,
"A block's child's next in flow's parent must be a block!");
for (line_iterator line = nifBlock->begin_lines(),
line_end = nifBlock->end_lines(); line != line_end; ++line) {
if (line->Contains(nextFrame)) {
line->MarkDirty();
break;
if (line->Contains(nextFrame)) {
line->MarkDirty();
break;
}
}
}
}
}
*aKeepReflowGoing = PR_FALSE;
// The bottom margin for a block is only applied on the last
// flow block. Since we just continued the child block frame,
// we know that line->mFirstChild is not the last flow block
// therefore zero out the running margin value.
*aKeepReflowGoing = PR_FALSE;
// The bottom margin for a block is only applied on the last
// flow block. Since we just continued the child block frame,
// we know that line->mFirstChild is not the last flow block
// therefore zero out the running margin value.
#ifdef NOISY_VERTICAL_MARGINS
ListTag(stdout);
printf(": reflow incomplete, frame=");
nsFrame::ListTag(stdout, frame);
printf(" prevBottomMargin=%d, setting to zero\n",
aState.mPrevBottomMargin);
ListTag(stdout);
printf(": reflow incomplete, frame=");
nsFrame::ListTag(stdout, frame);
printf(" prevBottomMargin=%d, setting to zero\n",
aState.mPrevBottomMargin);
#endif
aState.mPrevBottomMargin.Zero();
aState.mPrevBottomMargin.Zero();
}
else { // frame is complete but its overflow is not complete
// Disconnect the next-in-flow and put it in our overflow tracker
if (!madeContinuation &&
!(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) {
// It already exists, but as a normal next-in-flow, so we need
// to dig it out of the child lists.
nsContainerFrame* parent = static_cast<nsContainerFrame*>
(nextFrame->GetParent());
rv = parent->StealFrame(aState.mPresContext, nextFrame);
NS_ENSURE_SUCCESS(rv, rv);
}
else if (madeContinuation) {
frame->SetNextSibling(nextFrame->GetNextSibling());
nextFrame->SetNextSibling(nsnull);
}
// Put it in our overflow list
aState.mOverflowTracker.Insert(nextFrame, frameReflowStatus);
aState.mReflowStatus = NS_FRAME_MERGE_INCOMPLETE(frameReflowStatus,
aState.mReflowStatus);
#ifdef NOISY_VERTICAL_MARGINS
ListTag(stdout);
printf(": reflow complete but overflow incomplete for ");
nsFrame::ListTag(stdout, frame);
printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n",
aState.mPrevBottomMargin, collapsedBottomMargin.get());
#endif
aState.mPrevBottomMargin = collapsedBottomMargin;
}
}
else {
else { // frame is fully complete
#ifdef NOISY_VERTICAL_MARGINS
ListTag(stdout);
printf(": reflow complete for ");
@ -3052,7 +3133,7 @@ nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState,
// to our next-in-flow.
UndoSplitPlaceholders(aState, lastPlaceholder);
PushLines(aState, aLine.prev());
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
}
}
}
@ -3174,7 +3255,7 @@ nsBlockFrame::PushTruncatedPlaceholderLine(nsBlockReflowState& aState,
--prevLine;
PushLines(aState, prevLine);
aKeepReflowGoing = PR_FALSE;
aState.mReflowStatus = NS_FRAME_NOT_COMPLETE;
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
}
#ifdef DEBUG
@ -3927,7 +4008,7 @@ nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
// Stop reflow and whack the reflow status if reflow hasn't
// already been stopped.
if (*aKeepReflowGoing) {
aState.mReflowStatus |= NS_FRAME_NOT_COMPLETE;
NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus);
*aKeepReflowGoing = PR_FALSE;
}
return PR_TRUE;
@ -4946,7 +5027,8 @@ nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame)
// The containing block is always the parent of aFrame.
nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent();
// Remove aFrame from the appropriate list.
// Remove aFrame from the appropriate list.
if (display->IsAbsolutelyPositioned()) {
block->mAbsoluteContainer.RemoveFrame(block,
block->mAbsoluteContainer.GetChildListName(),
@ -5014,6 +5096,11 @@ nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, PRBool aDestroyFrames,
nsPresContext* presContext = PresContext();
nsIPresShell* presShell = presContext->PresShell();
if (NS_FRAME_IS_OVERFLOW_CONTAINER & aDeletedFrame->GetStateBits()) {
nsContainerFrame::DeleteNextInFlowChild(presContext, aDeletedFrame);
return NS_OK;
}
PRBool isPlaceholder = nsGkAtoms::placeholderFrame == aDeletedFrame->GetType();
if (isPlaceholder) {
nsFrameList* overflowPlaceholders = GetOverflowPlaceholders();
@ -5222,15 +5309,91 @@ found_frame:;
return RemoveBlockChild(aDeletedFrame, aDestroyFrames);
}
nsresult
nsBlockFrame::StealFrame(nsPresContext* aPresContext,
nsIFrame* aChild,
PRBool aForceNormal)
{
NS_PRECONDITION(aPresContext && aChild, "null pointer");
if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
&& !aForceNormal)
return nsContainerFrame::StealFrame(aPresContext, aChild);
// Find the line and the previous sibling that contains
// aChild; we also find the pointer to the line.
nsLineList::iterator line = mLines.begin(),
line_end = mLines.end();
PRBool searchingOverflowList = PR_FALSE;
nsIFrame* prevSibling = nsnull;
// Make sure we look in the overflow lines even if the normal line
// list is empty
TryAllLines(&line, &line_end, &searchingOverflowList);
while (line != line_end) {
nsIFrame* frame = line->mFirstChild;
PRInt32 n = line->GetChildCount();
while (--n >= 0) {
if (frame == aChild) {
// Disconnect from sibling list
if (prevSibling)
prevSibling->SetNextSibling(frame->GetNextSibling());
else
line->mFirstChild = frame->GetNextSibling();
frame->SetNextSibling(nsnull);
// Register removal with the line boxes
PRInt32 count = line->GetChildCount();
line->SetChildCount(--count);
if (count > 0) {
line->MarkDirty();
}
else {
// Remove the line box
nsLineBox* lineBox = line;
if (searchingOverflowList) {
// Erase line, but avoid making the overflow line list empty
nsLineList* lineList = RemoveOverflowLines();
lineList->erase(line);
if (!lineList->empty()) {
nsresult rv = SetOverflowLines(lineList);
NS_ENSURE_SUCCESS(rv, rv);
}
}
else {
mLines.erase(line);
}
lineBox->Destroy(aPresContext->PresShell());
if (line != line_end) {
// Line disappeared, so tell next line it may have to change position
line->MarkPreviousMarginDirty();
}
}
// Ok, we're done
return NS_OK;
}
prevSibling = frame;
frame = frame->GetNextSibling();
}
++line;
TryAllLines(&line, &line_end, &searchingOverflowList);
}
return NS_ERROR_UNEXPECTED;
}
void
nsBlockFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
nsIFrame* aNextInFlow)
{
nsIFrame* prevInFlow = aNextInFlow->GetPrevInFlow();
NS_PRECONDITION(prevInFlow, "bad next-in-flow");
NS_PRECONDITION(IsChild(aNextInFlow), "bad geometric parent");
DoRemoveFrame(aNextInFlow);
if (NS_FRAME_IS_OVERFLOW_CONTAINER & aNextInFlow->GetStateBits()) {
nsContainerFrame::DeleteNextInFlowChild(aPresContext, aNextInFlow);
}
else {
DoRemoveFrame(aNextInFlow);
}
}
////////////////////////////////////////////////////////////////////////
@ -5329,6 +5492,10 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
// if the height is constrained (bug 145305).
if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && (NS_UNCONSTRAINEDSIZE == availHeight))
aReflowStatus = NS_FRAME_COMPLETE;
//XXXfr Floats can't be overflow incomplete yet
if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aReflowStatus))
NS_FRAME_SET_INCOMPLETE(aReflowStatus);
if (NS_FRAME_IS_COMPLETE(aReflowStatus)) {
// Float is now complete, so delete the placeholder's next in
@ -5410,9 +5577,12 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
// get the containing block of prevPlaceholder which is our prev-in-flow
if (GetPrevInFlow()) {
// get the break type of the last line in mPrevInFlow
line_iterator endLine = --((nsBlockFrame*)GetPrevInFlow())->end_lines();
if (endLine->HasFloatBreakAfter()) {
aState.mFloatBreakType = endLine->GetBreakTypeAfter();
nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
line_iterator endLine = prevBlock->end_lines();
if (endLine != prevBlock->begin_lines()) {
--endLine;
if (endLine->HasFloatBreakAfter())
aState.mFloatBreakType = endLine->GetBreakTypeAfter();
}
}
else NS_ASSERTION(PR_FALSE, "no prev in flow");
@ -5427,11 +5597,15 @@ nsBlockFrame::ReflowFloat(nsBlockReflowState& aState,
PRIntn
nsBlockFrame::GetSkipSides() const
{
if (mState & NS_FRAME_IS_OVERFLOW_CONTAINER)
return (1 << NS_SIDE_TOP) | (1 << NS_SIDE_BOTTOM);
PRIntn skip = 0;
if (nsnull != GetPrevInFlow()) {
if (GetPrevInFlow()) {
skip |= 1 << NS_SIDE_TOP;
}
if (nsnull != GetNextInFlow()) {
nsIFrame* nif = GetNextInFlow();
if (nif && !(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
skip |= 1 << NS_SIDE_BOTTOM;
}
return skip;
@ -5629,7 +5803,11 @@ nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
#endif
DisplayBorderBackgroundOutline(aBuilder, aLists);
if (GetPrevInFlow()) {
DisplayOverflowContainers(aBuilder, aDirtyRect, aLists);
}
aBuilder->MarkFramesForDisplayList(this, mFloats.FirstChild(), aDirtyRect);
aBuilder->MarkFramesForDisplayList(this, mAbsoluteContainer.GetFirstChild(), aDirtyRect);
@ -5890,43 +6068,6 @@ InSiblingList(nsLineList& aLines, nsIFrame* aFrame)
return PR_FALSE;
}
PRBool
nsBlockFrame::IsChild(nsIFrame* aFrame)
{
// Continued out-of-flows don't satisfy InLineList(), continued out-of-flows
// and placeholders don't satisfy InSiblingList().
PRBool skipLineList = PR_FALSE;
PRBool skipSiblingList = PR_FALSE;
nsIFrame* prevInFlow = aFrame->GetPrevInFlow();
PRBool isPlaceholder = nsGkAtoms::placeholderFrame == aFrame->GetType();
if (prevInFlow) {
nsFrameState state = aFrame->GetStateBits();
skipLineList = (state & NS_FRAME_OUT_OF_FLOW);
skipSiblingList = isPlaceholder || (state & NS_FRAME_OUT_OF_FLOW);
}
if (isPlaceholder) {
nsFrameList* overflowPlaceholders = GetOverflowPlaceholders();
if (overflowPlaceholders && overflowPlaceholders->ContainsFrame(aFrame)) {
return PR_TRUE;
}
}
if (aFrame->GetParent() != (nsIFrame*)this) {
return PR_FALSE;
}
if ((skipLineList || InLineList(mLines, aFrame)) &&
(skipSiblingList || InSiblingList(mLines, aFrame))) {
return PR_TRUE;
}
nsLineList* overflowLines = GetOverflowLines();
if (overflowLines && (skipLineList || InLineList(*overflowLines, aFrame)) &&
(skipSiblingList || InSiblingList(*overflowLines, aFrame))) {
return PR_TRUE;
}
return PR_FALSE;
}
NS_IMETHODIMP
nsBlockFrame::VerifyTree() const
{

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

@ -79,12 +79,7 @@ class nsIntervalSet;
* Child list name indices
* @see #GetAdditionalChildListName()
*/
#define NS_BLOCK_FRAME_FLOAT_LIST_INDEX 0
#define NS_BLOCK_FRAME_BULLET_LIST_INDEX 1
#define NS_BLOCK_FRAME_OVERFLOW_LIST_INDEX 2
#define NS_BLOCK_FRAME_OVERFLOW_OOF_LIST_INDEX 3
#define NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX 4
#define NS_BLOCK_FRAME_LAST_LIST_INDEX NS_BLOCK_FRAME_ABSOLUTE_LIST_INDEX
#define NS_BLOCK_LIST_COUNT (NS_CONTAINER_LIST_COUNT_INCL_OC + 4)
/**
* Some invariants:
@ -186,6 +181,12 @@ public:
nscoord aX, nscoord aY, nsIFrame* aForChild,
PRBool aImmediate);
virtual nsIAtom* GetType() const;
virtual PRBool IsFrameOfType(PRUint32 aFlags) const
{
return nsContainerFrame::IsFrameOfType(aFlags &
~(nsIFrame::eCanContainOverflowContainers));
}
#ifdef DEBUG
NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;
NS_IMETHOD_(nsFrameState) GetDebugStateBits() const;
@ -241,6 +242,10 @@ public:
nsIAtom* aAttribute,
PRInt32 aModType);
virtual nsresult StealFrame(nsPresContext* aPresContext,
nsIFrame* aChild,
PRBool aForceNormal);
virtual void DeleteNextInFlowChild(nsPresContext* aPresContext,
nsIFrame* aNextInFlow);
@ -584,7 +589,6 @@ protected:
nsIFrame* LastChild();
#ifdef NS_DEBUG
PRBool IsChild(nsIFrame* aFrame);
void VerifyLines(PRBool aFinalCheckOK);
void VerifyOverflowSituation();
PRInt32 GetDepth() const;

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

@ -400,7 +400,7 @@ nsBlockReflowContext::ReflowBlock(const nsRect& aSpace,
// them now. Do not do this when a break-before is signaled because
// the frame is going to get reflowed again (and may end up wanting
// a next-in-flow where it ends up), unless it is an out of flow frame.
if (NS_FRAME_IS_COMPLETE(aFrameReflowStatus)) {
if (NS_FRAME_IS_FULLY_COMPLETE(aFrameReflowStatus)) {
nsIFrame* kidNextInFlow = mFrame->GetNextInFlow();
if (nsnull != kidNextInFlow) {
// Remove all of the childs next-in-flows. Make sure that we ask

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

@ -71,9 +71,12 @@ nsBlockReflowState::nsBlockReflowState(const nsHTMLReflowState& aReflowState,
mPrevBottomMargin(),
mLineNumber(0),
mFlags(0),
mFloatBreakType(NS_STYLE_CLEAR_NONE)
mFloatBreakType(NS_STYLE_CLEAR_NONE),
mOverflowTracker(aPresContext, aFrame)
{
SetFlag(BRS_ISFIRSTINFLOW, aFrame->GetPrevInFlow() == nsnull);
SetFlag(BRS_ISOVERFLOWCONTAINER,
!!(aFrame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER));
const nsMargin& borderPadding = BorderPadding();

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

@ -46,6 +46,7 @@
#include "nsBlockBandData.h"
#include "nsLineBox.h"
#include "nsFrameList.h"
#include "nsContainerFrame.h"
class nsBlockFrame;
@ -59,7 +60,8 @@ class nsBlockFrame;
#define BRS_HAVELINEADJACENTTOTOP 0x00000020
// Set when the block has the equivalent of NS_BLOCK_SPACE_MGR
#define BRS_SPACE_MGR 0x00000040
#define BRS_LASTFLAG BRS_SPACE_MGR
#define BRS_ISOVERFLOWCONTAINER 0x00000100
#define BRS_LASTFLAG BRS_ISOVERFLOWCONTAINER
class nsBlockReflowState {
public:
@ -123,6 +125,9 @@ public:
nsMargin result = mReflowState.mComputedBorderPadding;
if (!(mFlags & BRS_ISFIRSTINFLOW)) {
result.top = 0;
if (mFlags & BRS_ISOVERFLOWCONTAINER) {
result.bottom = 0;
}
}
return result;
}
@ -196,6 +201,9 @@ public:
// the overflow lines.
nsFrameList mOverflowPlaceholders;
// Track child overflow continuations.
nsOverflowContinuationTracker mOverflowTracker;
//----------------------------------------
// This state is "running" state updated by the reflow of each line

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

@ -75,7 +75,14 @@ public:
virtual nsIFrame* GetContentInsertionFrame() {
return GetFirstChild(nsnull)->GetContentInsertionFrame();
}
virtual nsresult StealFrame(nsPresContext* aPresContext,
nsIFrame* aChild,
PRBool aForceNormal)
{ // nsColumnSetFrame keeps overflow containers in main child list
return nsContainerFrame::StealFrame(aPresContext, aChild, PR_TRUE);
}
NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists);
@ -440,9 +447,9 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
// might be pullable back to this column. We can't skip if it's the last child
// because we need to obtain the bottom margin.
PRBool skipIncremental = !(GetStateBits() & NS_FRAME_IS_DIRTY)
&& !(child->GetStateBits() & NS_FRAME_IS_DIRTY)
&& !NS_SUBTREE_DIRTY(child)
&& child->GetNextSibling()
&& !(child->GetNextSibling()->GetStateBits() & NS_FRAME_IS_DIRTY);
&& !NS_SUBTREE_DIRTY(child->GetNextSibling());
// If we need to pull up content from the prev-in-flow then this is not just
// a height shrink. The prev in flow will have set the dirty bit.
// Check the overflow rect YMost instead of just the child's content height. The child
@ -457,8 +464,11 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
MoveChildTo(this, child, childOrigin);
// If this is the last frame then make sure we get the right status
if (child->GetNextSibling()) {
aStatus = NS_FRAME_NOT_COMPLETE;
nsIFrame* kidNext = child->GetNextSibling();
if (kidNext) {
aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
? NS_FRAME_OVERFLOW_INCOMPLETE
: NS_FRAME_NOT_COMPLETE;
} else {
aStatus = mLastFrameStatus;
}
@ -536,7 +546,7 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
// Build a continuation column if necessary
nsIFrame* kidNextInFlow = child->GetNextInFlow();
if (NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) {
if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)) {
NS_ASSERTION(!kidNextInFlow, "next in flow should have been deleted");
break;
} else {
@ -557,6 +567,21 @@ nsColumnSetFrame::ReflowChildren(nsHTMLReflowMetrics& aDesiredSize,
break;
}
}
// Make sure we reflow a next-in-flow when it switches between being
// normal or overflow container
if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
if (!(kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
reflowNext = PR_TRUE;
kidNextInFlow->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
}
}
else if (kidNextInFlow->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
aStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
reflowNext = PR_TRUE;
kidNextInFlow->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
}
if (columnCount >= aConfig.mBalanceColCount) {
// No more columns allowed here. Stop.

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

@ -21,6 +21,7 @@
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
* Elika J. Etemad ("fantasai") <fantasai@inkedblade.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -39,6 +40,7 @@
/* base class #1 for rendering objects that have child lists */
#include "nsContainerFrame.h"
#include "nsHTMLContainerFrame.h"
#include "nsIContent.h"
#include "nsIDocument.h"
#include "nsPresContext.h"
@ -218,6 +220,10 @@ nsContainerFrame::RemoveFrame(nsIAtom* aListName,
// remove the frame from its parents list and generate a reflow
// command.
nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
//XXXfr probably should use StealFrame here. I'm not sure if we need to
// check the overflow lists atm, but we'll need a prescontext lookup
// for overflow containers once we can split abspos elements with
// inline containing blocks.
parent->mFrames.DestroyFrame(aOldFrame);
aOldFrame = oldFrameNextContinuation;
if (aOldFrame) {
@ -244,11 +250,25 @@ nsContainerFrame::Destroy()
// Delete the primary child list
mFrames.DestroyFrames();
// Destroy overflow frames now
nsFrameList overflowFrames(GetOverflowFrames(PresContext(), PR_TRUE));
// Destroy auxiliary frame lists
nsPresContext* prescontext = PresContext();
nsFrameList overflowFrames(GetOverflowFrames(prescontext, PR_TRUE));
overflowFrames.DestroyFrames();
if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
nsFrameList* frameList = RemovePropTableFrames(prescontext,
nsGkAtoms::overflowContainersProperty);
if (frameList)
frameList->Destroy();
frameList = RemovePropTableFrames(prescontext,
nsGkAtoms::excessOverflowContainersProperty);
if (frameList)
frameList->Destroy();
}
if (IsGeneratedContentFrame()) {
// Make sure all the content nodes for the generated content inside
// this frame know it's going away.
@ -286,19 +306,38 @@ nsContainerFrame::GetFirstChild(nsIAtom* aListName) const
return mFrames.FirstChild();
} else if (nsGkAtoms::overflowList == aListName) {
return GetOverflowFrames(PresContext(), PR_FALSE);
} else if (nsGkAtoms::overflowContainersList == aListName) {
nsFrameList* list = GetPropTableFrames(PresContext(),
nsGkAtoms::overflowContainersProperty);
return (list) ? list->FirstChild() : nsnull;
} else if (nsGkAtoms::excessOverflowContainersList == aListName) {
nsFrameList* list = GetPropTableFrames(PresContext(),
nsGkAtoms::excessOverflowContainersProperty);
return (list) ? list->FirstChild() : nsnull;
} else {
return nsnull;
}
}
#define NS_CONTAINER_FRAME_OVERFLOW_LIST_INDEX 0
#define NS_CONTAINER_FRAME_OVERFLOW_CONTAINERS_LIST_INDEX 1
#define NS_CONTAINER_FRAME_EXCESS_OVERFLOW_CONTAINERS_LIST_INDEX 2
// If adding/removing lists, don't forget to update count in .h file
nsIAtom*
nsContainerFrame::GetAdditionalChildListName(PRInt32 aIndex) const
{
if (aIndex == 0) {
if (NS_CONTAINER_FRAME_OVERFLOW_LIST_INDEX == aIndex)
return nsGkAtoms::overflowList;
} else {
return nsnull;
else if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
if (NS_CONTAINER_FRAME_OVERFLOW_CONTAINERS_LIST_INDEX == aIndex)
return nsGkAtoms::overflowContainersList;
else if (NS_CONTAINER_FRAME_EXCESS_OVERFLOW_CONTAINERS_LIST_INDEX == aIndex)
return nsGkAtoms::excessOverflowContainersList;
}
return nsnull;
}
/////////////////////////////////////////////////////////////////////////////
@ -652,7 +691,8 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
nscoord aX,
nscoord aY,
PRUint32 aFlags,
nsReflowStatus& aStatus)
nsReflowStatus& aStatus,
nsOverflowContinuationTracker* aTracker)
{
NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state");
@ -676,7 +716,8 @@ nsContainerFrame::ReflowChild(nsIFrame* aKidFrame,
// If the reflow was successful and the child frame is complete, delete any
// next-in-flows
if (NS_SUCCEEDED(result) && NS_FRAME_IS_COMPLETE(aStatus)) {
if (NS_SUCCEEDED(result) && NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
if (aTracker) aTracker->Finish(aKidFrame);
nsIFrame* kidNextInFlow = aKidFrame->GetNextInFlow();
if (nsnull != kidNextInFlow) {
// Remove all of the childs next-in-flows. Make sure that we ask
@ -788,6 +829,167 @@ nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame,
return aKidFrame->DidReflow(aPresContext, aReflowState, NS_FRAME_REFLOW_FINISHED);
}
nsresult
nsContainerFrame::ReflowOverflowContainerChildren(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsRect& aOverflowRect,
PRUint32 aFlags,
nsReflowStatus& aStatus)
{
NS_PRECONDITION(aPresContext, "null pointer");
nsresult rv = NS_OK;
nsFrameList* overflowContainers =
GetPropTableFrames(aPresContext,
nsGkAtoms::overflowContainersProperty);
NS_ASSERTION(!(overflowContainers && GetPrevInFlow()
&& static_cast<nsContainerFrame*>(GetPrevInFlow())
->GetPropTableFrames(aPresContext,
nsGkAtoms::excessOverflowContainersProperty)),
"conflicting overflow containers lists");
if (!overflowContainers) {
// Drain excess from previnflow
nsContainerFrame* prev = (nsContainerFrame*) GetPrevInFlow();
if (prev) {
nsFrameList* excessFrames =
prev->RemovePropTableFrames(aPresContext,
nsGkAtoms::excessOverflowContainersProperty);
if (excessFrames) {
nsFrameList reparenter;
reparenter.InsertFrames(this, nsnull, excessFrames->FirstChild());
nsHTMLContainerFrame::ReparentFrameViewList(aPresContext,
excessFrames->FirstChild(),
prev, this);
overflowContainers = excessFrames;
rv = SetPropTableFrames(aPresContext, overflowContainers,
nsGkAtoms::overflowContainersProperty);
if (NS_FAILED(rv)) {
excessFrames->DestroyFrames();
delete excessFrames;
return rv;
}
}
}
}
if (!overflowContainers)
return NS_OK; // nothing to reflow
nsOverflowContinuationTracker tracker(aPresContext, this, PR_FALSE);
for (nsIFrame* frame = overflowContainers->FirstChild(); frame;
frame = frame->GetNextSibling()) {
if (NS_SUBTREE_DIRTY(frame)) {
// Get prev-in-flow
nsIFrame* prevInFlow = frame->GetPrevInFlow();
NS_ASSERTION(prevInFlow,
"overflow container frame must have a prev-in-flow");
NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER,
"overflow container frame must have overflow container bit set");
nsRect prevRect = prevInFlow->GetRect();
// Initialize reflow params
nsSize availSpace(prevRect.width, aReflowState.availableHeight);
nsHTMLReflowMetrics desiredSize;
nsHTMLReflowState frameState(aPresContext, aReflowState,
frame, availSpace);
nsReflowStatus frameStatus = NS_FRAME_COMPLETE;
// Reflow
rv = ReflowChild(frame, aPresContext, desiredSize, frameState,
prevRect.x, 0, aFlags, frameStatus);
NS_ENSURE_SUCCESS(rv, rv);
//XXXfr Do we need to override any shrinkwrap effects here?
// e.g. desiredSize.width = prevRect.width;
rv = FinishReflowChild(frame, aPresContext, &frameState, desiredSize,
prevRect.x, 0, aFlags);
NS_ENSURE_SUCCESS(rv, rv);
// Handle continuations
NS_ASSERTION(NS_FRAME_IS_COMPLETE(frameStatus),
"overflow container frames can't be incomplete, only overflow-incomplete");
if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) {
// Acquire a next-in-flow, creating it if necessary
nsIFrame* nif = frame->GetNextInFlow();
if (!nif) {
rv = nsHTMLContainerFrame::CreateNextInFlow(aPresContext, this,
frame, nif);
NS_ENSURE_SUCCESS(rv, rv);
NS_ASSERTION(frameStatus & NS_FRAME_REFLOW_NEXTINFLOW,
"Someone forgot a REFLOW_NEXTINFLOW flag");
frame->SetNextSibling(nif->GetNextSibling());
nif->SetNextSibling(nsnull);
}
else if (!(nif->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
// used to be a normal next-in-flow; steal it from the child list
rv = static_cast<nsContainerFrame*>(nif->GetParent())
->StealFrame(aPresContext, nif);
NS_ENSURE_SUCCESS(rv, rv);
}
tracker.Insert(nif, frameStatus);
}
aStatus = NS_FRAME_MERGE_INCOMPLETE(aStatus, frameStatus);
// At this point it would be nice to assert !frame->GetOverflowRect().IsEmpty(),
// but we have some unsplittable frames that, when taller than
// availableHeight will push zero-height content into a next-in-flow.
}
else {
tracker.Skip(frame, aStatus);
}
ConsiderChildOverflow(aOverflowRect, frame);
}
return NS_OK;
}
void
nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists)
{
nsFrameList* overflowconts = GetPropTableFrames(PresContext(),
nsGkAtoms::overflowContainersProperty);
if (overflowconts) {
for (nsIFrame* frame = overflowconts->FirstChild(); frame;
frame = frame->GetNextSibling()) {
BuildDisplayListForChild(aBuilder, frame, aDirtyRect, aLists);
}
}
}
nsresult
nsContainerFrame::StealFrame(nsPresContext* aPresContext,
nsIFrame* aChild,
PRBool aForceNormal)
{
PRBool removed = PR_TRUE;
if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
&& !aForceNormal) {
// Try removing from the overflow container list
if (!RemovePropTableFrame(aPresContext, aChild,
nsGkAtoms::overflowContainersProperty)) {
// It must be in the excess overflow container list
removed = RemovePropTableFrame(aPresContext, aChild,
nsGkAtoms::excessOverflowContainersProperty);
}
}
else {
if (!mFrames.RemoveFrame(aChild)) {
// We didn't find the child in the parent's principal child list.
// Maybe it's on the overflow list?
nsFrameList frameList(GetOverflowFrames(aPresContext, PR_TRUE));
removed = frameList.RemoveFrame(aChild);
if (frameList.NotEmpty()) {
nsresult rv = SetOverflowFrames(aPresContext, frameList.FirstChild());
NS_ENSURE_SUCCESS(rv, rv);
}
}
}
return (removed) ? NS_OK : NS_ERROR_UNEXPECTED;
}
/**
* Remove and delete aNextInFlow and its next-in-flows. Updates the sibling and flow
* pointers
@ -820,21 +1022,8 @@ nsContainerFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
nsSplittableFrame::BreakFromPrevFlow(aNextInFlow);
// Take the next-in-flow out of the parent's child list
PRBool result = mFrames.RemoveFrame(aNextInFlow);
if (!result) {
// We didn't find the child in the parent's principal child list.
// Maybe it's on the overflow list?
nsFrameList overflowFrames(GetOverflowFrames(aPresContext, PR_TRUE));
if (overflowFrames.IsEmpty() || !overflowFrames.RemoveFrame(aNextInFlow)) {
NS_ASSERTION(result, "failed to remove frame");
}
// Set the overflow property again
if (overflowFrames.NotEmpty()) {
SetOverflowFrames(aPresContext, overflowFrames.FirstChild());
}
}
nsresult rv = StealFrame(aPresContext, aNextInFlow);
NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failure");
// Delete the next-in-flow frame and its descendants.
aNextInFlow->Destroy();
@ -846,15 +1035,14 @@ nsContainerFrame::DeleteNextInFlowChild(nsPresContext* aPresContext,
* Get the frames on the overflow list
*/
nsIFrame*
nsContainerFrame::GetOverflowFrames(nsPresContext* aPresContext,
nsContainerFrame::GetOverflowFrames(nsPresContext* aPresContext,
PRBool aRemoveProperty) const
{
nsPropertyTable *propTable = aPresContext->PropertyTable();
if (aRemoveProperty) {
return (nsIFrame*) propTable->UnsetProperty(this,
nsGkAtoms::overflowProperty);
nsGkAtoms::overflowProperty);
}
return (nsIFrame*) propTable->GetProperty(this,
nsGkAtoms::overflowProperty);
}
@ -893,6 +1081,67 @@ nsContainerFrame::SetOverflowFrames(nsPresContext* aPresContext,
return rv;
}
// Destructor function for the proptable-stored framelists
static void
DestroyFrameList(void* aFrame,
nsIAtom* aPropertyName,
void* aPropertyValue,
void* aDtorData)
{
if (aPropertyValue)
static_cast<nsFrameList*>(aPropertyValue)->Destroy();
}
nsFrameList*
nsContainerFrame::GetPropTableFrames(nsPresContext* aPresContext,
nsIAtom* aPropID) const
{
nsPropertyTable* propTable = aPresContext->PropertyTable();
return static_cast<nsFrameList*>(propTable->GetProperty(this, aPropID));
}
nsFrameList*
nsContainerFrame::RemovePropTableFrames(nsPresContext* aPresContext,
nsIAtom* aPropID) const
{
nsPropertyTable* propTable = aPresContext->PropertyTable();
return static_cast<nsFrameList*>(propTable->UnsetProperty(this, aPropID));
}
PRBool
nsContainerFrame::RemovePropTableFrame(nsPresContext* aPresContext,
nsIFrame* aFrame,
nsIAtom* aPropID) const
{
nsFrameList* frameList = RemovePropTableFrames(aPresContext, aPropID);
if (!frameList || !frameList->RemoveFrame(aFrame)) {
// Failed to remove frame
return PR_FALSE;
}
if (frameList->IsEmpty()) {
// Removed frame and now list is empty. Delete it.
delete frameList;
}
else {
// Removed frame, but list not empty. Put it back.
SetPropTableFrames(aPresContext, frameList, aPropID);
}
return PR_TRUE;
}
nsresult
nsContainerFrame::SetPropTableFrames(nsPresContext* aPresContext,
nsFrameList* aFrameList,
nsIAtom* aPropID) const
{
NS_PRECONDITION(aPresContext && aPropID && aFrameList, "null ptr");
nsresult rv = aPresContext->PropertyTable()->SetProperty(this, aPropID,
aFrameList, DestroyFrameList, nsnull);
NS_ASSERTION(rv != NS_PROPTABLE_PROP_OVERWRITTEN, "existing framelist");
return rv;
}
/**
* Push aFromChild and its next siblings to the next-in-flow. Change the
* geometric parent of each frame that's pushed. If there is no next-in-flow
@ -977,6 +1226,163 @@ nsContainerFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
return result;
}
nsOverflowContinuationTracker::nsOverflowContinuationTracker(nsPresContext* aPresContext,
nsContainerFrame* aFrame,
PRBool aSkipOverflowContainerChildren)
: mOverflowContList(nsnull),
mPrevOverflowCont(nsnull),
mSentry(nsnull),
mParent(aFrame),
mSkipOverflowContainerChildren(aSkipOverflowContainerChildren)
{
NS_PRECONDITION(aFrame, "null frame pointer");
nsContainerFrame* next = static_cast<nsContainerFrame*>
(aFrame->GetNextInFlow());
if (next) {
mOverflowContList =
next->GetPropTableFrames(aPresContext,
nsGkAtoms::overflowContainersProperty);
if (mOverflowContList) {
mParent = next;
SetUpListWalker();
}
}
else {
mOverflowContList =
mParent->GetPropTableFrames(aPresContext,
nsGkAtoms::excessOverflowContainersProperty);
if (mOverflowContList) {
SetUpListWalker();
}
}
}
/**
* Helper function to walk past overflow continuations whose prev-in-flow
* isn't a normal child and to set mSentry and mPrevOverflowCont correctly.
*/
void
nsOverflowContinuationTracker::SetUpListWalker()
{
NS_ASSERTION(!mSentry && !mPrevOverflowCont,
"forgot to reset mSentry or mPrevOverflowCont");
if (mOverflowContList) {
nsIFrame* cur = mOverflowContList->FirstChild();
if (mSkipOverflowContainerChildren) {
while (cur && (cur->GetPrevInFlow()->GetStateBits()
& NS_FRAME_IS_OVERFLOW_CONTAINER)) {
mPrevOverflowCont = cur;
cur = cur->GetNextSibling();
}
}
if (cur) {
mSentry = cur->GetPrevInFlow();
}
}
}
/**
* Helper function to step forward through the overflow continuations list.
* Sets mSentry and mPrevOverflowCont as appropriate.
* May only be called when we have already set up an mOverflowContList;
* mOverflowContList cannot be null.
*/
void
nsOverflowContinuationTracker::StepForward()
{
NS_PRECONDITION(mOverflowContList, "null list");
// Step forward
if (mPrevOverflowCont) {
mPrevOverflowCont = mPrevOverflowCont->GetNextSibling();
}
else {
mPrevOverflowCont = mOverflowContList->FirstChild();
}
// Set up the sentry
mSentry = (mPrevOverflowCont->GetNextSibling())
? mPrevOverflowCont->GetNextSibling()->GetPrevInFlow()
: nsnull;
}
nsresult
nsOverflowContinuationTracker::Insert(nsIFrame* aOverflowCont,
nsReflowStatus& aReflowStatus)
{
NS_PRECONDITION(aOverflowCont, "null frame pointer");
NS_PRECONDITION(aOverflowCont->GetPrevInFlow(),
"overflow containers must have a prev-in-flow");
nsresult rv = NS_OK;
if (!mSentry || aOverflowCont != mSentry->GetNextInFlow()) {
// Not in our list, so we need to add it
nsPresContext* presContext = aOverflowCont->PresContext();
if ((aOverflowCont->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)
&& mParent != aOverflowCont->GetParent()) {
// aOverflowCont is in some other frame's overflow container list,
// steal it first
rv = static_cast<nsContainerFrame*>(aOverflowCont->GetParent())
->StealFrame(presContext, aOverflowCont);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
NS_ASSERTION(!(aOverflowCont->GetStateBits()
& NS_FRAME_IS_OVERFLOW_CONTAINER),
"overflow containers out of order or bad parent");
aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
}
if (!mOverflowContList) {
mOverflowContList = new nsFrameList();
rv = mParent->SetPropTableFrames(presContext,
mOverflowContList, nsGkAtoms::excessOverflowContainersProperty);
NS_ENSURE_SUCCESS(rv, rv);
SetUpListWalker();
}
if (aOverflowCont->GetParent() != mParent) {
nsHTMLContainerFrame::ReparentFrameView(presContext, aOverflowCont,
aOverflowCont->GetParent(),
mParent);
}
mOverflowContList->InsertFrame(mParent, mPrevOverflowCont, aOverflowCont);
aReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW;
}
// If we need to reflow it, mark it dirty
if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW)
aOverflowCont->AddStateBits(NS_FRAME_IS_DIRTY);
// It's in our list, just step forward
StepForward();
NS_ASSERTION(mPrevOverflowCont == aOverflowCont,
"OverflowContTracker logic error");
return rv;
}
void
nsOverflowContinuationTracker::Finish(nsIFrame* aChild)
{
NS_PRECONDITION(aChild, "null ptr");
NS_PRECONDITION(aChild->GetNextInFlow(),
"supposed to call Next *before* deleting next-in-flow!");
if (aChild == mSentry) {
// Make sure we drop all references if this was the only frame
// in the overflow containers list
if (mOverflowContList->FirstChild() == aChild->GetNextInFlow()
&& !aChild->GetNextInFlow()->GetNextSibling()) {
mOverflowContList = nsnull;
mPrevOverflowCont = nsnull;
mSentry = nsnull;
mParent = static_cast<nsContainerFrame*>(aChild->GetParent());
}
else {
// Don't move the mPrevOverflowCont, but shift the sentry
// The intervening overflow continuation will be deleted by our caller
nsIFrame* sentryCont = aChild->GetNextInFlow()->GetNextSibling();
mSentry = (sentryCont) ? sentryCont->GetPrevInFlow() : nsnull;
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Debugging

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

@ -20,6 +20,7 @@
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Elika J. Etemad ("fantasai") <fantasai@inkedblade.net>
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
@ -44,6 +45,15 @@
#include "nsFrameList.h"
#include "nsLayoutUtils.h"
/**
* Child list name indices
* @see #GetAdditionalChildListName()
*/
#define NS_CONTAINER_LIST_COUNT_SANS_OC 1
// for frames that don't use overflow containers
#define NS_CONTAINER_LIST_COUNT_INCL_OC 3
// for frames that support overflow containers
// Option flags for ReflowChild() and FinishReflowChild()
// member functions
#define NS_FRAME_NO_MOVE_VIEW 0x0001
@ -51,6 +61,8 @@
#define NS_FRAME_NO_SIZE_VIEW 0x0004
#define NS_FRAME_NO_VISIBILITY 0x0008
class nsOverflowContinuationTracker;
/**
* Implementation of a container frame.
*/
@ -150,14 +162,15 @@ public:
* NS_FRAME_NO_MOVE_FRAME - don't move the frame. aX and aY are ignored in this
* case. Also implies NS_FRAME_NO_MOVE_VIEW
*/
nsresult ReflowChild(nsIFrame* aKidFrame,
nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nscoord aX,
nscoord aY,
PRUint32 aFlags,
nsReflowStatus& aStatus);
nsresult ReflowChild(nsIFrame* aKidFrame,
nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nscoord aX,
nscoord aY,
PRUint32 aFlags,
nsReflowStatus& aStatus,
nsOverflowContinuationTracker* aTracker = nsnull);
/**
* The second half of frame reflow. Does the following:
@ -186,7 +199,98 @@ public:
static void PositionChildViews(nsIFrame* aFrame);
// ==========================================================================
/* Overflow containers are continuation frames that hold overflow. They
* are created when the frame runs out of computed height, but still has
* too much content to fit in the availableHeight. The parent creates a
* continuation as usual, but marks it as NS_FRAME_IS_OVERFLOW_CONTAINER
* and adds it to its next-in-flow's overflow container list, either by
* adding it directly or by putting it in its own excess overflow containers
* list (to be drained by the next-in-flow when it calls
* ReflowOverflowContainerChildren). The parent continues reflow as if
* the frame was complete once it ran out of computed height, but returns
* either an NS_FRAME_NOT_COMPLETE or NS_FRAME_OVERFLOW_INCOMPLETE reflow
* status to request a next-in-flow. The parent's next-in-flow is then
* responsible for calling ReflowOverflowContainerChildren to (drain and)
* reflow these overflow continuations. Overflow containers do not affect
* other frames' size or position during reflow (but do affect their
* parent's overflow area).
*
* Overflow container continuations are different from normal continuations
* in that
* - more than one child of the frame can have its next-in-flow broken
* off and pushed into the frame's next-in-flow
* - new continuations may need to be spliced into the middle of the list
* or deleted continuations slipped out
* e.g. A, B, C are all fixed-size containers on one page, all have
* overflow beyond availableHeight, and content is dynamically added
* and removed from B
* As a result, it is not possible to simply prepend the new continuations
* to the old list as with the overflowProperty mechanism. To avoid
* complicated list splicing, the code assumes only one overflow containers
* list exists for a given frame: either its own overflowContainersProperty
* or its prev-in-flow's excessOverflowContainersProperty, not both.
*
* The nsOverflowContinuationTracker helper class should be used for tracking
* overflow containers and adding them to the appropriate list.
* See nsBlockFrame::Reflow for a sample implementation.
*/
friend class nsOverflowContinuationTracker;
/**
* Reflow overflow container children. They are invisible to normal reflow
* (i.e. don't affect sizing or placement of other children) and inherit
* width and horizontal position from their prev-in-flow.
*
* This method
* 1. Pulls excess overflow containers from the prev-in-flow and adds
* them to our overflow container list
* 2. Reflows all our overflow container kids
* 3. Expands aOverflowRect as necessary to accomodate these children.
* 4. Sets aStatus's NS_FRAME_OVERFLOW_IS_INCOMPLETE flag (along with
* NS_FRAME_REFLOW_NEXTINFLOW as necessary) if any overflow children
* are incomplete and
* 5. Prepends a list of their continuations to our excess overflow
* container list, to be drained into our next-in-flow when it is
* reflowed.
*
* The caller is responsible for tracking any new overflow container
* continuations it makes, removing them from its child list, and
* making sure they are stored properly in the overflow container lists.
* The nsOverflowContinuationTracker helper class should be used for this.
*
* (aFlags just gets passed through to ReflowChild)
*/
nsresult ReflowOverflowContainerChildren(nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsRect& aOverflowRect,
PRUint32 aFlags,
nsReflowStatus& aStatus);
/**
* Removes aChild without destroying it and without requesting reflow.
* Continuations are not affected. Checks the primary and overflow
* or overflow containers and excess overflow containers lists, depending
* on whether the NS_FRAME_IS_OVERFLOW_CONTAINER flag is set. Does not
* check any other auxiliary lists.
* Returns NS_ERROR_UNEXPECTED if we failed to remove aChild.
* Returns other error codes if we failed to put back a proptable list.
* If aForceNormal is true, only checks the primary and overflow lists
* even when the NS_FRAME_IS_OVERFLOW_CONTAINER flag is set.
*/
virtual nsresult StealFrame(nsPresContext* aPresContext,
nsIFrame* aChild,
PRBool aForceNormal = PR_FALSE);
/**
* Add overflow containers to the display list
*/
void DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists);
/**
* Builds display lists for the children. The background
* of each child is placed in the Content() list (suitable for inline
@ -218,12 +322,19 @@ protected:
const nsDisplayListSet& aLists,
PRUint32 aFlags = 0);
// ==========================================================================
/* Overflow Frames are frames that did not fit and must be pulled by
* our next-in-flow during its reflow. (The same concept for overflow
* containers is called "excess frames". We should probably make the
* names match.)
*/
/**
* Get the frames on the overflow list
*/
nsIFrame* GetOverflowFrames(nsPresContext* aPresContext,
PRBool aRemoveProperty) const;
/**
* Set the overflow list
*/
@ -259,7 +370,162 @@ protected:
nsIFrame* aFromChild,
nsIFrame* aPrevSibling);
// ==========================================================================
/*
* Convenience methods for nsFrameLists stored in the
* PresContext's proptable
*/
/**
* Get the PresContext-stored nsFrameList named aPropID for this frame.
* May return null.
*/
nsFrameList* GetPropTableFrames(nsPresContext* aPresContext,
nsIAtom* aPropID) const;
/**
* Remove and return the PresContext-stored nsFrameList named aPropID for
* this frame. May return null.
*/
nsFrameList* RemovePropTableFrames(nsPresContext* aPresContext,
nsIAtom* aPropID) const;
/**
* Remove aFrame from the PresContext-stored nsFrameList named aPropID
* for this frame, deleting the list if it is now empty.
* Return true if the aFrame was successfully removed,
* Return false otherwise.
*/
PRBool RemovePropTableFrame(nsPresContext* aPresContext,
nsIFrame* aFrame,
nsIAtom* aPropID) const;
/**
* Set the PresContext-stored nsFrameList named aPropID for this frame
* to the given aFrameList, which must not be null.
*/
nsresult SetPropTableFrames(nsPresContext* aPresContext,
nsFrameList* aFrameList,
nsIAtom* aPropID) const;
// ==========================================================================
nsFrameList mFrames;
};
/**
* Helper class for tracking overflow container continuations during reflow.
*
* A frame is related to two sets of overflow containers: those that /are/
* its own children, and those that are /continuations/ of its children.
* This tracker walks through those continuations (the frame's NIF's children)
* and their prev-in-flows (a subset of the frame's normal and overflow
* container children) in parallel. It allows the reflower to synchronously
* walk its overflow continuations while it loops through and reflows its
* children. This makes it possible to insert new continuations at the correct
* place in the overflow containers list.
*
* The reflower is expected to loop through its children in the same order it
* looped through them the last time (if there was a last time).
* For each child, the reflower should either
* - call Skip for the child if was not reflowed in this pass
* - call Insert for the overflow continuation if the child was reflowed
* but has incomplete overflow
* - call Finished for the child if it was reflowed in this pass but
* is either complete or has a normal next-in-flow. This call can
* be skipped if the child did not previously have an overflow
* continuation.
*/
class nsOverflowContinuationTracker {
public:
/**
* Initializes an nsOverflowContinuationTracker to help track overflow
* continuations of aFrame's children. Typically invoked on 'this'.
*
* Don't set aSkipOverflowContainerChildren to PR_FALSE unless you plan
* to walk your own overflow container children. (Usually they are handled
* by calling ReflowOverflowContainerChildren.)
*/
nsOverflowContinuationTracker(nsPresContext* aPresContext,
nsContainerFrame* aFrame,
PRBool aSkipOverflowContainerChildren = PR_TRUE);
/**
* This function adds an overflow continuation to our running list and
* sets its NS_FRAME_IS_OVERFLOW_CONTAINER flag.
*
* aReflowStatus should preferably be specific to the recently-reflowed
* child and not influenced by any of its siblings' statuses. This
* function sets the NS_FRAME_IS_DIRTY bit on aOverflowCont if it needs
* to be reflowed. (Its need for reflow depends on changes to its
* prev-in-flow, not to its parent--for whom it is invisible, reflow-wise.)
*
* The caller MUST disconnect the frame from its parent's child list
* if it was not previously an NS_FRAME_IS_OVERFLOW_CONTAINER (because
* StealFrame is much more inefficient than disconnecting in place
* during Reflow, which the caller is able to do but we are not).
*
* The caller MUST NOT disconnect the frame from its parent's
* child list if it is already an NS_FRAME_IS_OVERFLOW_CONTAINER.
* (In this case we will disconnect and reconnect it ourselves.)
*/
nsresult Insert(nsIFrame* aOverflowCont,
nsReflowStatus& aReflowStatus);
/**
* This function should be called for each child that is reflowed
* but no longer has an overflow continuation. It increments our
* walker and makes sure we drop any dangling pointers to its
* next-in-flow. This function MUST be called before stealing or
* deleting aChild's next-in-flow.
*/
void Finish(nsIFrame* aChild);
/**
* This function should be called for each child that isn't reflowed.
* It increments our walker and sets the NS_FRAME_OVERFLOW_INCOMPLETE
* reflow flag if it encounters an overflow continuation so that our
* next-in-flow doesn't get prematurely deleted. It MUST be called on
* each unreflowed child that has an overflow container continuation;
* it MAY be called on other children, but it isn't necessary (doesn't
* do anything).
*/
void Skip(nsIFrame* aChild, nsReflowStatus& aReflowStatus)
{
NS_PRECONDITION(aChild, "null ptr");
if (aChild == mSentry) {
StepForward();
aReflowStatus = NS_FRAME_MERGE_INCOMPLETE(aReflowStatus,
NS_FRAME_OVERFLOW_INCOMPLETE);
}
}
private:
void SetUpListWalker();
void StepForward();
/* We hold a pointer to either the next-in-flow's overflow containers list
or, if that doesn't exist, our frame's excess overflow containers list.
We need to make sure that we drop that pointer if the list becomes
empty and is deleted elsewhere. */
nsFrameList* mOverflowContList;
/* We hold a pointer to the most recently-reflowed child that has an
overflow container next-in-flow. We do this because it's a known
good point; this pointer won't be deleted on us. We can use it to
recover our place in the list. */
nsIFrame* mPrevOverflowCont;
/* This is a pointer to the next overflow container's prev-in-flow, which
is (or should be) a child of our frame. When we hit this, we will need
to increment this walker to the next overflow container. */
nsIFrame* mSentry;
/* Parent of all frames in mOverflowContList. If our mOverflowContList
is an excessOverflowContainersProperty, then this our frame (the frame
that was passed in to our constructor). Otherwise this is that frame's
next-in-flow, and our mOverflowContList is mParent's
overflowContainersProperty */
nsContainerFrame* mParent;
/* Tells SetUpListWalker whether or not to walk us past any continuations
of overflow containers. */
PRBool mSkipOverflowContainerChildren;
};
#endif /* nsContainerFrame_h___ */

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

@ -7337,7 +7337,7 @@ void nsFrame::DisplayReflowExit(nsPresContext* aPresContext,
DR_state->PrettyUC(aMetrics.height, height);
printf("Reflow d=%s,%s ", width, height);
if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
printf("status=0x%x", aStatus);
}
if (aFrame->GetStateBits() & NS_FRAME_OUTSIDE_CHILDREN) {

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

@ -51,6 +51,13 @@
#include "nsBidiPresUtils.h"
#endif // IBMBIDI
void
nsFrameList::Destroy()
{
DestroyFrames();
delete this;
}
void
nsFrameList::DestroyFrames()
{

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

@ -65,6 +65,9 @@ public:
void DestroyFrames();
// Delete this and destroy all its frames
void Destroy();
void SetFrames(nsIFrame* aFrameList) {
mFirstChild = aFrameList;
#ifdef DEBUG

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

@ -177,6 +177,13 @@ enum {
// frames.
NS_FRAME_GENERATED_CONTENT = 0x00000040,
// If this bit is set the frame is a continuation that is holding overflow,
// i.e. it is a next-in-flow created to hold overflow after the box's
// height has ended. This means the frame should be a) at the top of the
// page and b) invisible: no borders, zero height, ignored in margin
// collapsing, etc. See nsContainerFrame.h
NS_FRAME_IS_OVERFLOW_CONTAINER = 0x00000080,
// If this bit is set, then the frame has been moved out of the flow,
// e.g., it is absolutely positioned or floated
NS_FRAME_OUT_OF_FLOW = 0x00000100,
@ -287,16 +294,31 @@ enum nsSpread {
//----------------------------------------------------------------------
/**
* Reflow status returned by the reflow methods.
* Reflow status returned by the reflow methods. There are three
* completion statuses, represented by two bit flags.
*
* NS_FRAME_COMPLETE means the frame is fully complete.
*
* NS_FRAME_NOT_COMPLETE bit flag means the frame does not map all its
* content, and that the parent frame should create a continuing frame.
* If this bit isn't set it means the frame does map all its content.
* This bit is mutually exclusive with NS_FRAME_OVERFLOW_INCOMPLETE.
*
* NS_FRAME_OVERFLOW_INCOMPLETE bit flag means that the frame has
* overflow that is not complete, but its own box is complete.
* (This happens when content overflows a fixed-height box.)
* The reflower should place and size the frame and continue its reflow,
* but needs to create an overflow container as a continuation for this
* frame. See nsContainerFrame.h for more information.
* This bit is mutually exclusive with NS_FRAME_NOT_COMPLETE.
*
* Please use the SET and MERGE macros below for handling
* NS_FRAME_NOT_COMPLETE and NS_FRAME_OVERFLOW_INCOMPLETE.
*
* NS_FRAME_REFLOW_NEXTINFLOW bit flag means that the next-in-flow is
* dirty, and also needs to be reflowed. This status only makes sense
* for a frame that is not complete, i.e. you wouldn't set both
* NS_FRAME_COMPLETE and NS_FRAME_REFLOW_NEXTINFLOW
* NS_FRAME_COMPLETE and NS_FRAME_REFLOW_NEXTINFLOW.
*
* The low 8 bits of the nsReflowStatus are reserved for future extensions;
* the remaining 24 bits are zero (and available for extensions; however
@ -307,9 +329,10 @@ enum nsSpread {
*/
typedef PRUint32 nsReflowStatus;
#define NS_FRAME_COMPLETE 0 // Note: not a bit!
#define NS_FRAME_NOT_COMPLETE 0x1
#define NS_FRAME_REFLOW_NEXTINFLOW 0x2
#define NS_FRAME_COMPLETE 0 // Note: not a bit!
#define NS_FRAME_NOT_COMPLETE 0x1
#define NS_FRAME_REFLOW_NEXTINFLOW 0x2
#define NS_FRAME_OVERFLOW_INCOMPLETE 0x4
#define NS_FRAME_IS_COMPLETE(status) \
(0 == ((status) & NS_FRAME_NOT_COMPLETE))
@ -317,6 +340,29 @@ typedef PRUint32 nsReflowStatus;
#define NS_FRAME_IS_NOT_COMPLETE(status) \
(0 != ((status) & NS_FRAME_NOT_COMPLETE))
#define NS_FRAME_OVERFLOW_IS_INCOMPLETE(status) \
(0 != ((status) & NS_FRAME_OVERFLOW_INCOMPLETE))
#define NS_FRAME_IS_FULLY_COMPLETE(status) \
(NS_FRAME_IS_COMPLETE(status) && !NS_FRAME_OVERFLOW_IS_INCOMPLETE(status))
// These macros set or switch incompete statuses without touching th
// NS_FRAME_REFLOW_NEXTINFLOW bit.
#define NS_FRAME_SET_INCOMPLETE(status) \
status = status & ~NS_FRAME_OVERFLOW_INCOMPLETE | NS_FRAME_NOT_COMPLETE
#define NS_FRAME_SET_OVERFLOW_INCOMPLETE(status) \
status = status & ~NS_FRAME_NOT_COMPLETE | NS_FRAME_OVERFLOW_INCOMPLETE
// Combines two statuses and returns the most severe bits of the pair
#define NS_FRAME_MERGE_INCOMPLETE(status1, status2) \
( (NS_FRAME_REFLOW_NEXTINFLOW & (status1 | status2)) \
| ( (NS_FRAME_NOT_COMPLETE & (status1 | status2)) \
? NS_FRAME_NOT_COMPLETE \
: NS_FRAME_OVERFLOW_INCOMPLETE & (status1 | status2) \
) \
)
// This macro tests to see if an nsReflowStatus is an error value
// or just a regular return value
#define NS_IS_REFLOW_ERROR(_status) (PRInt32(_status) < 0)
@ -1463,6 +1509,7 @@ public:
// requires nsHTMLReflowState::mLineLayout.
eLineParticipant = 1 << 6,
eXULBox = 1 << 7,
eCanContainOverflowContainers = 1 << 8,
// These are to allow nsFrame::Init to assert that IsFrameOfType

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

@ -636,7 +636,7 @@ nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext,
}
nsIFrame* nextFrame = aFrame->GetNextSibling();
if (nextFrame) {
aStatus |= NS_FRAME_NOT_COMPLETE;
NS_FRAME_SET_INCOMPLETE(aStatus);
PushFrames(aPresContext, nextFrame, aFrame);
}
else if (nsnull != GetNextInFlow()) {
@ -645,7 +645,7 @@ nsInlineFrame::ReflowInlineFrame(nsPresContext* aPresContext,
nsInlineFrame* nextInFlow = (nsInlineFrame*) GetNextInFlow();
while (nsnull != nextInFlow) {
if (nextInFlow->mFrames.NotEmpty()) {
aStatus |= NS_FRAME_NOT_COMPLETE;
NS_FRAME_SET_INCOMPLETE(aStatus);
break;
}
nextInFlow = (nsInlineFrame*) nextInFlow->GetNextInFlow();

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

@ -35,6 +35,9 @@
*
* ***** END LICENSE BLOCK ***** */
#include "nsPageContentFrame.h"
#include "nsPageFrame.h"
#include "nsCSSFrameConstructor.h"
#include "nsHTMLContainerFrame.h"
#include "nsHTMLParts.h"
#include "nsIContent.h"
#include "nsPresContext.h"
@ -52,15 +55,27 @@ NS_NewPageContentFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
return new (aPresShell) nsPageContentFrame(aContext);
}
NS_IMETHODIMP nsPageContentFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
NS_IMETHODIMP
nsPageContentFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowMetrics& aDesiredSize,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aStatus)
{
DO_GLOBAL_REFLOW_COUNT("nsPageContentFrame");
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
aStatus = NS_FRAME_COMPLETE; // initialize out parameter
nsPageContentFrame* prevPageContentFrame = static_cast<nsPageContentFrame*>
(GetPrevInFlow());
if (prevPageContentFrame) {
nsIFrame* overflow = prevPageContentFrame->GetOverflowFrames(aPresContext, PR_TRUE);
nsHTMLContainerFrame::ReparentFrameViewList(aPresContext, overflow, prevPageContentFrame, this);
// Prepend overflow to the page content frame. There may already be
// children placeholders which don't get reflowed but must not be
// lost until the page content frame is destroyed.
mFrames.InsertFrames(this, nsnull, overflow);
}
// Resize our frame allowing it only to be as big as we are
// XXX Pay attention to the page's border and padding...
if (mFrames.NotEmpty()) {
@ -73,6 +88,28 @@ NS_IMETHODIMP nsPageContentFrame::Reflow(nsPresContext* aPresContext,
// Reflow the page content area to get the child's desired size
ReflowChild(frame, aPresContext, aDesiredSize, kidReflowState, 0, 0, 0, aStatus);
if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) {
nsIFrame* nextFrame = frame->GetNextInFlow();
NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW,
"If it's incomplete and has no nif yet, it must flag a nif reflow.");
if (!nextFrame) {
nsresult rv = nsHTMLContainerFrame::CreateNextInFlow(aPresContext,
this, frame, nextFrame);
NS_ENSURE_SUCCESS(rv, rv);
frame->SetNextSibling(nextFrame->GetNextSibling());
nextFrame->SetNextSibling(nsnull);
SetOverflowFrames(aPresContext, nextFrame);
// Root overflow containers will be normal children of
// the pageContentFrame, but that's ok because there
// aren't any other frames we need to isolate them from
// during reflow.
}
if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
nextFrame->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
NS_FRAME_SET_INCOMPLETE(aStatus); // page frames can't have overflow
}
}
// The document element's background should cover the entire canvas, so
// take into account the combined area and any space taken up by
// absolutely positioned elements
@ -97,7 +134,7 @@ NS_IMETHODIMP nsPageContentFrame::Reflow(nsPresContext* aPresContext,
// Place and size the child
FinishReflowChild(frame, aPresContext, &kidReflowState, aDesiredSize, 0, 0, 0);
NS_ASSERTION(aPresContext->IsDynamic() || !NS_FRAME_IS_COMPLETE(aStatus) ||
NS_ASSERTION(aPresContext->IsDynamic() || !NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
!frame->GetNextInFlow(), "bad child flow list");
}
// Reflow our fixed frames

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

@ -90,34 +90,9 @@ NS_IMETHODIMP nsPageFrame::Reflow(nsPresContext* aPresContext,
DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
aStatus = NS_FRAME_COMPLETE; // initialize out parameter
// Do we have any children?
// XXX We should use the overflow list instead...
nsIFrame* firstFrame = mFrames.FirstChild();
nsPageContentFrame* contentPage = static_cast<nsPageContentFrame*>(firstFrame);
NS_ASSERTION(contentPage, "There should always be a content page");
NS_ASSERTION(nsGkAtoms::pageContentFrame == firstFrame->GetType(),
"This frame isn't a pageContentFrame");
if (contentPage && GetPrevInFlow() && !contentPage->GetFirstChild(nsnull)) {
nsPageFrame* prevPage = static_cast<nsPageFrame*>(GetPrevInFlow());
nsPageContentFrame* prevContentPage = static_cast<nsPageContentFrame*>(prevPage->mFrames.FirstChild());
nsIFrame* prevLastChild = prevContentPage->mFrames.LastChild();
// Create a continuing child of the previous page's last child
nsIFrame* newFrame;
nsresult rv = aPresContext->PresShell()->FrameConstructor()->
CreateContinuingFrame(aPresContext, prevLastChild,
contentPage, &newFrame);
if (NS_FAILED(rv)) {
return rv;
}
// Make the new area frame the 1st child of the page content frame. There may already be
// children placeholders which don't get reflowed but must not be destroyed until the
// page content frame is destroyed.
contentPage->mFrames.InsertFrame(contentPage, nsnull, newFrame);
}
NS_ASSERTION(mFrames.FirstChild() &&
nsGkAtoms::pageContentFrame == mFrames.FirstChild()->GetType(),
"pageFrame must have a pageContentFrame child");
// Resize our frame allowing it only to be as big as we are
// XXX Pay attention to the page's border and padding...
@ -163,7 +138,7 @@ NS_IMETHODIMP nsPageFrame::Reflow(nsPresContext* aPresContext,
// Place and size the child
FinishReflowChild(frame, aPresContext, &kidReflowState, aDesiredSize, xc, yc, 0);
NS_ASSERTION(!NS_FRAME_IS_COMPLETE(aStatus) ||
NS_ASSERTION(!NS_FRAME_IS_FULLY_COMPLETE(aStatus) ||
!frame->GetNextInFlow(), "bad child flow list");
}
PR_PL(("PageFrame::Reflow %p ", this));

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

@ -316,7 +316,7 @@ ViewportFrame::Reflow(nsPresContext* aPresContext,
// Just reflow all the fixed-pos frames.
rv = mFixedContainer.Reflow(this, aPresContext, reflowState,
reflowState.ComputedWidth(),
reflowState.ComputedWidth(),
reflowState.mComputedHeight,
PR_TRUE, PR_TRUE); // XXX could be optimized

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

@ -302,6 +302,13 @@ fails-if(MOZ_WIDGET_TOOLKIT=="cocoa") == 372037-1.html 372037-1-ref.html # bug 3
== 377918.html 377918-ref.html
== 378535-1.html 378535-1-ref.html
== 379316-1.html 379316-1-ref.html
== 379349-1a.xhtml 379349-1-ref.xhtml
== 379349-1b.xhtml 379349-1-ref.xhtml
== 379349-1c.xhtml 379349-1-ref.xhtml
== 379349-2a.xhtml 379349-2-ref.xhtml
== 379349-2b.xhtml 379349-2-ref.xhtml
== 379349-3a.xhtml 379349-3-ref.xhtml
== 379349-3b.xhtml 379349-3-ref.xhtml
random-if(MOZ_WIDGET_TOOLKIT=="cocoa") == 379316-2.html 379316-2-ref.html # bug 379786
== 379328-1.html 379328-1-ref.html
== 380004-1.html 380004-1-ref.html

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

@ -851,6 +851,12 @@ NS_METHOD nsTableCellFrame::Reflow(nsPresContext* aPresContext,
ReflowChild(firstKid, aPresContext, kidSize, kidReflowState,
kidOrigin.x, kidOrigin.y, 0, aStatus);
if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aStatus)) {
// Don't pass OVERFLOW_INCOMPLETE through tables until they can actually handle it
//XXX should paginate overflow as overflow, but not in this patch (bug 379349)
NS_FRAME_SET_INCOMPLETE(aStatus);
printf("Set table cell incomplete %p\n", this);
}
if (GetStateBits() & NS_FRAME_IS_DIRTY) {
Invalidate(GetOverflowRect(), PR_FALSE);
}