Mozilla/mozilla/layout/tables/nsTableOuterFrame.cpp
buster 192406dc56 added an optimization that skips pass2 cell reflows when the new available width is unchanged
from the prior available width.  Some real-world test cases sped up
1-2 orders of magnitude for resize reflow.  Initial reflow is unchanged because
we still need to get pass1 metrics on all content.
fixed nsCSSBlockFrame.cpp to take margins into account when computing parent's available width
fixed distribution of excess space to table cells when table is bigger than the content it holds.
not quite finished yet, but better.


git-svn-id: svn://10.0.0.236/trunk@4905 18797224-902f-48f8-a5cc-f745e15eee43
1998-07-02 17:40:56 +00:00

1602 lines
57 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 "nsTableOuterFrame.h"
#include "nsTableFrame.h"
#include "nsITableContent.h"
#include "nsTableContent.h"
#include "nsBodyFrame.h"
#include "nsIReflowCommand.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIPresContext.h"
#include "nsIRenderingContext.h"
#include "nsCSSRendering.h"
#include "nsIContent.h"
#include "nsIContentDelegate.h"
#include "nsCSSLayout.h"
#include "nsVoidArray.h"
#include "nsIReflowCommand.h"
#include "nsIPtr.h"
#include "prinrval.h"
#ifdef NS_DEBUG
static PRBool gsDebug = PR_FALSE;
static PRBool gsTiming = PR_FALSE;
//#define NOISY
//#define NOISY_FLOW
#define NOISY_MARGINS
#else
static const PRBool gsDebug = PR_FALSE;
static const PRBool gsTiming = PR_FALSE;
#endif
static NS_DEFINE_IID(kITableContentIID, NS_ITABLECONTENT_IID);
NS_DEF_PTR(nsIStyleContext);
NS_DEF_PTR(nsIContent);
struct OuterTableReflowState {
// The presentation context
nsIPresContext *pc;
// Our reflow state
const nsReflowState& reflowState;
// The total available size (computed from the parent)
nsSize availSize;
// The available size for the inner table frame
nsSize innerTableMaxSize;
// Margin tracking information
nscoord prevMaxPosBottomMargin;
nscoord prevMaxNegBottomMargin;
// Flags for whether the max size is unconstrained
PRBool unconstrainedWidth;
PRBool unconstrainedHeight;
// Running y-offset
nscoord y;
// Flags for where we are in the content
PRBool firstRowGroup;
PRBool processingCaption;
OuterTableReflowState(nsIPresContext* aPresContext,
const nsReflowState& aReflowState)
: reflowState(aReflowState)
{
pc = aPresContext;
availSize.width = reflowState.maxSize.width;
availSize.height = reflowState.maxSize.height;
prevMaxPosBottomMargin = 0;
prevMaxNegBottomMargin = 0;
y=0; // border/padding/margin???
unconstrainedWidth = PRBool(aReflowState.maxSize.width == NS_UNCONSTRAINEDSIZE);
unconstrainedHeight = PRBool(aReflowState.maxSize.height == NS_UNCONSTRAINEDSIZE);
firstRowGroup = PR_TRUE;
processingCaption = PR_FALSE;
innerTableMaxSize.width=0;
innerTableMaxSize.height=0;
}
~OuterTableReflowState() {
}
};
/* ----------- nsTableOuterFrame ---------- */
/**
*/
nsTableOuterFrame::nsTableOuterFrame(nsIContent* aContent, nsIFrame* aParentFrame)
: nsContainerFrame(aContent, aParentFrame),
mInnerTableFrame(nsnull),
mCaptionFrames(nsnull),
mBottomCaptions(nsnull),
mMinCaptionWidth(0),
mMaxCaptionWidth(0),
mDesiredSize(nsnull)
{
}
nsTableOuterFrame::~nsTableOuterFrame()
{
if (nsnull!=mCaptionFrames)
delete mCaptionFrames;
if (nsnull!=mBottomCaptions)
delete mBottomCaptions;
}
NS_METHOD nsTableOuterFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
// for debug...
if (nsIFrame::GetShowFrameBorders()) {
aRenderingContext.SetColor(NS_RGB(255,0,0));
aRenderingContext.DrawRect(0, 0, mRect.width, mRect.height);
}
PaintChildren(aPresContext, aRenderingContext, aDirtyRect);
return NS_OK;
}
PRBool nsTableOuterFrame::NeedsReflow(const nsSize& aMaxSize)
{
PRBool result=PR_TRUE;
if (nsnull!=mInnerTableFrame)
result = mInnerTableFrame->NeedsReflow(aMaxSize);
return result;
}
// Recover the reflow state to what it should be if aKidFrame is about
// to be reflowed
nsresult nsTableOuterFrame::RecoverState(OuterTableReflowState& aState,
nsIFrame* aKidFrame)
{
// Get aKidFrame's previous sibling
nsIFrame* prevKidFrame = nsnull;
for (nsIFrame* frame = mFirstChild; frame != aKidFrame;) {
prevKidFrame = frame;
frame->GetNextSibling(frame);
}
if (nsnull != prevKidFrame) {
nsRect rect;
// Set our running y-offset
prevKidFrame->GetRect(rect);
aState.y = rect.YMost();
// Adjust the available space
if (PR_FALSE == aState.unconstrainedHeight) {
aState.availSize.height -= aState.y;
}
// Get the previous frame's bottom margin
const nsStyleSpacing* kidSpacing;
prevKidFrame->GetStyleData(eStyleStruct_Spacing, (nsStyleStruct *&)kidSpacing);
nsMargin margin;
kidSpacing->CalcMarginFor(prevKidFrame, margin);
if (margin.bottom < 0) {
aState.prevMaxNegBottomMargin = -margin.bottom;
} else {
aState.prevMaxPosBottomMargin = margin.bottom;
}
}
// Set the inner table max size
nsSize innerTableSize(0,0);
mInnerTableFrame->GetSize(innerTableSize);
aState.innerTableMaxSize.width = innerTableSize.width;
aState.innerTableMaxSize.height = aState.reflowState.maxSize.height;
return NS_OK;
}
nsresult nsTableOuterFrame::AdjustSiblingsAfterReflow(nsIPresContext* aPresContext,
OuterTableReflowState& aState,
nsIFrame* aKidFrame,
nscoord aDeltaY)
{
nsIFrame* lastKidFrame = aKidFrame;
if (aDeltaY != 0) {
// Move the frames that follow aKidFrame by aDeltaY
nsIFrame* kidFrame;
aKidFrame->GetNextSibling(kidFrame);
while (nsnull != kidFrame) {
nsPoint origin;
// XXX We can't just slide the child if it has a next-in-flow
kidFrame->GetOrigin(origin);
origin.y += aDeltaY;
// XXX We need to send move notifications to the frame...
kidFrame->WillReflow(*aPresContext);
kidFrame->MoveTo(origin.x, origin.y);
// Get the next frame
lastKidFrame = kidFrame;
kidFrame->GetNextSibling(kidFrame);
}
} else {
// Get the last frame
LastChild(lastKidFrame);
}
// Update our running y-offset to reflect the bottommost child
nsRect rect;
lastKidFrame->GetRect(rect);
aState.y = rect.YMost();
// Get the bottom margin for the last child frame
const nsStyleSpacing* kidSpacing;
lastKidFrame->GetStyleData(eStyleStruct_Spacing, (nsStyleStruct *&)kidSpacing);
nsMargin margin;
kidSpacing->CalcMarginFor(lastKidFrame, margin);
if (margin.bottom < 0) {
aState.prevMaxNegBottomMargin = -margin.bottom;
} else {
aState.prevMaxPosBottomMargin = margin.bottom;
}
return NS_OK;
}
nsresult nsTableOuterFrame::IncrementalReflow(nsIPresContext* aPresContext,
OuterTableReflowState& aState,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
// Get the next frame in the reflow chain
nsIFrame* kidFrame;
aReflowState.reflowCommand->GetNext(kidFrame);
// Recover our reflow state
RecoverState(aState, kidFrame);
// Pass along the reflow command
// XXX Check if we're the target...
nsSize kidMaxElementSize(0,0);
nsSize* pKidMaxElementSize = (nsnull != aDesiredSize.maxElementSize) ?
&kidMaxElementSize : nsnull;
nsReflowMetrics kidSize(pKidMaxElementSize);
nsRect oldKidRect;
kidFrame->GetRect(oldKidRect);
SetReflowState(aState, kidFrame);
// Get the top margin for the child
const nsStyleSpacing* kidSpacing;
kidFrame->GetStyleData(eStyleStruct_Spacing, (nsStyleStruct *&)kidSpacing);
nsMargin kidMargin;
kidSpacing->CalcMarginFor(kidFrame, kidMargin);
nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMargin);
nscoord bottomMargin = kidMargin.bottom;
// Figure out the amount of available size for the child (subtract
// off the top margin we are going to apply to it)
if (PR_FALSE == aState.unconstrainedHeight) {
aState.availSize.height -= topMargin;
}
// Subtract off for left and right margin
if (PR_FALSE == aState.unconstrainedWidth) {
aState.availSize.width -= kidMargin.left + kidMargin.right;
}
// Now do the reflow
aState.y += topMargin;
kidFrame->WillReflow(*aPresContext);
kidFrame->MoveTo(kidMargin.left, aState.y);
nsReflowState kidReflowState(kidFrame, aState.reflowState, aState.availSize);
if (kidFrame != mInnerTableFrame) {
// Reflow captions to the width of the inner table
kidReflowState.maxSize.width = aState.innerTableMaxSize.width;
}
mInnerTableFrame->SetReflowPass(nsTableFrame::kPASS_INCREMENTAL);
aStatus = ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState,
pKidMaxElementSize, aState);
// Place the child frame after taking into account its margin
nsRect kidRect (kidMargin.left, aState.y, kidSize.width, kidSize.height);
PlaceChild(aState, kidFrame, kidRect, aDesiredSize.maxElementSize, kidMaxElementSize);
if (bottomMargin < 0) {
aState.prevMaxNegBottomMargin = -bottomMargin;
} else {
aState.prevMaxPosBottomMargin = bottomMargin;
}
// XXX We're not correctly dealing with maxElementSize...
// Adjust the frames that follow
return AdjustSiblingsAfterReflow(aPresContext, aState, kidFrame,
kidRect.YMost() - oldKidRect.YMost());
}
/**
* Reflow is a 2-step process.
* In the first step, we lay out all of the captions and the inner table in NS_UNCONSTRAINEDSIZE space.
* This gives us absolute minimum and maximum widths.
* In the second step, we force all the captions and the table to the width of the widest component,
* given the table's width constraints.
* With the widths known, we reflow the captions and table.
* NOTE: for breaking across pages, this method has to account for table content that is not laid out
* linearly vis a vis the frames. That is, content hierarchy and the frame hierarchy do not match.
*/
NS_METHOD nsTableOuterFrame::Reflow(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
if (PR_TRUE==gsDebug)
printf("%p: nsTableOuterFrame::Reflow : maxSize=%d,%d\n",
this, aReflowState.maxSize.width, aReflowState.maxSize.height);
PRIntervalTime startTime;
if (gsTiming) {
startTime = PR_IntervalNow();
}
#ifdef NS_DEBUG
// replace with a check that does not assume linear placement of children
// PreReflowCheck();
#endif
// Initialize out parameters
if (nsnull != aDesiredSize.maxElementSize) {
aDesiredSize.maxElementSize->width = 0;
aDesiredSize.maxElementSize->height = 0;
}
aStatus = NS_FRAME_COMPLETE;
// Initialize our local reflow state
OuterTableReflowState state(aPresContext, aReflowState);
if (eReflowReason_Incremental == aReflowState.reason) {
IncrementalReflow(aPresContext, state, aDesiredSize, aReflowState, aStatus);
} else {
if (eReflowReason_Initial == aReflowState.reason) {
// Set up our kids. They're already present, on an overflow list,
// or there are none so we'll create them now
MoveOverflowToChildList();
if (nsnull==mFirstChild) {
CreateChildFrames(aPresContext);
}
// XXX Add a comment on why mInnerTableFrame would be nsnull at this point?
// You would think that the call to CreateChildFrames() would have created
// the inner table frame...
if (nsnull!=mPrevInFlow && nsnull==mInnerTableFrame)
{ // if I am a continuing frame, my inner table is my prev-in-flow's mInnerTableFrame's next-in-flow
CreateInnerTableFrame(aPresContext);
}
// lay out captions pass 1
aStatus = ResizeReflowCaptionsPass1(aPresContext, state, PR_TRUE);
}
// at this point, we must have at least one child frame, and we must have an inner table frame
NS_ASSERTION(nsnull!=mFirstChild, "no children");
NS_ASSERTION(nsnull!=mInnerTableFrame, "no mInnerTableFrame");
if (nsnull==mFirstChild || nsnull==mInnerTableFrame) {
//ERROR!
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
// lay out inner table, if required
nsReflowState innerTableReflowState(mInnerTableFrame, aReflowState, aReflowState.maxSize);
if (PR_FALSE==mInnerTableFrame->IsFirstPassValid())
{
// we treat the table as if we've never seen the layout data before
mInnerTableFrame->SetReflowPass(nsTableFrame::kPASS_FIRST);
aStatus = mInnerTableFrame->ResizeReflowPass1(aPresContext, aDesiredSize,
innerTableReflowState, aStatus);
}
mInnerTableFrame->SetReflowPass(nsTableFrame::kPASS_SECOND);
// assign table width info only if the inner table frame is a first-in-flow
nsIFrame* prevInFlow;
mInnerTableFrame->GetPrevInFlow(prevInFlow);
if (nsnull==prevInFlow)
{
// assign column widths, and assign aMaxElementSize->width
mInnerTableFrame->BalanceColumnWidths(aPresContext, innerTableReflowState,
innerTableReflowState.maxSize,
aDesiredSize.maxElementSize);
// assign table width
mInnerTableFrame->SetTableWidth(aPresContext);
}
// inner table max is now the computed width and assigned height
nsSize innerTableSize(0,0);
mInnerTableFrame->GetSize(innerTableSize);
state.innerTableMaxSize.width = innerTableSize.width;
state.innerTableMaxSize.height = aReflowState.maxSize.height;
// Reflow the child frames
PRBool reflowMappedOK = PR_TRUE;
if (nsnull != mFirstChild) {
reflowMappedOK = ReflowMappedChildren(aPresContext, state, nsnull);
if (PR_FALSE == reflowMappedOK) {
aStatus = NS_FRAME_NOT_COMPLETE;
}
}
// Did we successfully relow our mapped children?
if (PR_TRUE == reflowMappedOK) {
// Any space left?
if (state.availSize.height <= 0) {
// No space left. Don't try to pull-up children or reflow unmapped
if (NextChildOffset() < mContent->ChildCount()) {
aStatus = NS_FRAME_NOT_COMPLETE;
}
}
else {
// this is where any other kind of frame would call PullUpChildren
// but that logic doesn't seem to work with tables because of the way
// captions are done. captions can come before or after the inner table,
// making compares between nextChildOffset and mContent->ChildCount unsuitable
/*
PRInt32 nextChildOffset = NextChildOffset();
if ( nextChildOffset < mContent->ChildCount()) {
// Try and pull-up some children from a next-in-flow
if (PullUpChildren(aPresContext, state, aDesiredSize.maxElementSize)) {
// If we still have unmapped children then create some new frames
NS_ABORT(); // huge error for tables!
} else {
// We were unable to pull-up all the existing frames from the next in flow
aStatus = NS_FRAME_NOT_COMPLETE;
}
}
*/
}
}
}
if (NS_FRAME_IS_COMPLETE(aStatus)) {
// Don't forget to add in the bottom margin from our last child.
// Only add it in if there's room for it.
nscoord margin = state.prevMaxPosBottomMargin -
state.prevMaxNegBottomMargin;
if (state.availSize.height >= margin) {
state.y += margin;
}
}
// Return our desired rect
//NS_ASSERTION(0<state.y, "illegal height after reflow");
//NS_ASSERTION(0<state.innerTableMaxSize.width, "illegal width after reflow");
aDesiredSize.width = state.innerTableMaxSize.width;
/* if we're incomplete, take up all the remaining height so we don't waste time
* trying to lay out in a slot that we know isn't tall enough to fit our minimum.
* otherwise, we're as tall as our kids want us to be */
if (NS_FRAME_IS_NOT_COMPLETE(aStatus))
aDesiredSize.height = aReflowState.maxSize.height;
else
aDesiredSize.height = state.y;
if (gsDebug==PR_TRUE)
{
if (nsnull!=aDesiredSize.maxElementSize)
printf("%p: Outer frame Reflow complete, returning %s with aDesiredSize = %d,%d and aMaxElementSize=%d,%d\n",
this, NS_FRAME_IS_COMPLETE(aStatus)? "Complete" : "Not Complete",
aDesiredSize.width, aDesiredSize.height,
aDesiredSize.maxElementSize->width, aDesiredSize.maxElementSize->height);
else
printf("%p: Outer frame Reflow complete, returning %s with aDesiredSize = %d,%d and NSNULL aMaxElementSize\n",
this, NS_FRAME_IS_COMPLETE(aStatus)? "Complete" : "Not Complete",
aDesiredSize.width, aDesiredSize.height);
}
#ifdef NS_DEBUG
// replace with a check that does not assume linear placement of children
// PostReflowCheck(status);
#endif
if (gsTiming) {
PRIntervalTime endTime = PR_IntervalNow();
printf("Table reflow took %ld ticks for frame %d\n",
endTime-startTime, this);/* XXX need to use LL_* macros! */
}
return NS_OK;
}
// Collapse child's top margin with previous bottom margin
nscoord nsTableOuterFrame::GetTopMarginFor(nsIPresContext* aCX,
OuterTableReflowState& aState,
const nsMargin& aKidMargin)
{
nscoord margin;
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
if ((margin = aKidMargin.top) < 0) {
maxNegTopMargin = -margin;
} else {
maxPosTopMargin = margin;
}
nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
margin = maxPos - maxNeg;
return margin;
}
// Position and size aKidFrame and update our reflow state. The origin of
// aKidRect is relative to the upper-left origin of our frame, and includes
// any left/top margin.
void nsTableOuterFrame::PlaceChild( OuterTableReflowState& aState,
nsIFrame* aKidFrame,
const nsRect& aKidRect,
nsSize* aMaxElementSize,
nsSize& aKidMaxElementSize)
{
if (PR_TRUE==gsDebug)
printf ("outer table place child: %p with aKidRect %d %d %d %d\n",
aKidFrame, aKidRect.x, aKidRect.y,
aKidRect.width, aKidRect.height);
// Place and size the child
aKidFrame->SetRect(aKidRect);
// Adjust the running y-offset
aState.y += aKidRect.height;
// If our height is constrained then update the available height
if (PR_FALSE == aState.unconstrainedHeight) {
aState.availSize.height -= aKidRect.height;
}
/* Update the maximum element size, which is the sum of:
* the maxElementSize of our first row
* plus the maxElementSize of the top caption if we include it
* plus the maxElementSize of the bottom caption if we include it
*/
if (PR_TRUE==aState.processingCaption || PR_TRUE==aState.firstRowGroup)
{
if (PR_FALSE==aState.processingCaption)
aState.firstRowGroup = PR_FALSE;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = aKidMaxElementSize.width;
aMaxElementSize->height = aKidMaxElementSize.height;
}
}
}
/**
* Reflow the frames we've already created
*
* @param aPresContext presentation context to use
* @param aState current inline state
* @return true if we successfully reflowed all the mapped children and false
* otherwise, e.g. we pushed children to the next in flow
*/
PRBool nsTableOuterFrame::ReflowMappedChildren( nsIPresContext* aPresContext,
OuterTableReflowState& aState,
nsSize* aMaxElementSize)
{
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
#ifdef NOISY
ListTag(stdout);
printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
mChildCount,
mFirstContentOffset, mLastContentOffset,
(mLastContentIsComplete ? 'T' : 'F'));
#ifdef NOISY_FLOW
{
nsTableOuterFrame* flow = (nsTableOuterFrame*) mNextInFlow;
while (flow != 0) {
printf(" %p: [%d,%d,%c]\n",
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
(flow->mLastContentIsComplete ? 'T' : 'F'));
flow = (nsTableOuterFrame*) flow->mNextInFlow;
}
}
#endif
#endif
NS_PRECONDITION(nsnull != mFirstChild, "no children");
PRInt32 childCount = 0;
nsIFrame* prevKidFrame = nsnull;
// Remember our original mLastContentIsComplete so that if we end up
// having to push children, we have the correct value to hand to
// PushChildren.
PRBool originalLastContentIsComplete = mLastContentIsComplete;
nsSize kidMaxElementSize(0,0);
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
PRBool result = PR_TRUE;
aState.availSize.width = aState.innerTableMaxSize.width;
for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) {
nsReflowMetrics kidSize(pKidMaxElementSize);
kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0;
nsReflowStatus status;
SetReflowState(aState, kidFrame);
if (PR_TRUE==gsDebug) printf ("ReflowMappedChildren: %p with state = %s\n",
kidFrame, aState.processingCaption?"caption":"inner");
// Get top margin for this kid
const nsStyleSpacing* kidSpacing;
kidFrame->GetStyleData(eStyleStruct_Spacing, (nsStyleStruct *&)kidSpacing);
nsMargin kidMargin;
kidSpacing->CalcMarginFor(kidFrame, kidMargin);
nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMargin);
nscoord bottomMargin = kidMargin.bottom;
// Figure out the amount of available size for the child (subtract
// off the top margin we are going to apply to it)
if (PR_FALSE == aState.unconstrainedHeight) {
aState.availSize.height -= topMargin;
}
// Subtract off for left and right margin
if (PR_FALSE == aState.unconstrainedWidth) {
aState.availSize.width -= kidMargin.left + kidMargin.right;
}
// Only skip the reflow if this is not our first child and we are
// out of space.
if ((kidFrame == mFirstChild) || (aState.availSize.height > 0)) {
// Reflow the child into the available space
kidFrame->WillReflow(*aPresContext);
kidFrame->MoveTo(kidMargin.left, aState.y + topMargin);
nsReflowState kidReflowState(kidFrame, aState.reflowState, kidFrame == mInnerTableFrame ?
aState.innerTableMaxSize : aState.availSize,
eReflowReason_Resize);
status = ReflowChild(kidFrame, aPresContext, kidSize,
kidReflowState, pKidMaxElementSize, aState);
}
// Did the child fit?
if ((kidSize.height > aState.availSize.height) && (kidFrame != mFirstChild)) {
// The child is too tall to fit in the available space, and it's
// not our first child
// Since we are giving the next-in-flow our last child, we
// give it our original mLastContentIsComplete too (in case we
// are pushing into an empty next-in-flow)
PushChildren(kidFrame, prevKidFrame, originalLastContentIsComplete);
SetLastContentOffset(prevKidFrame);
result = PR_FALSE;
break;
}
// Place the child after taking into account it's margin
aState.y += topMargin;
nsRect kidRect (kidMargin.left, aState.y, kidSize.width, kidSize.height);
PlaceChild(aState, kidFrame, kidRect, aMaxElementSize, kidMaxElementSize);
if (bottomMargin < 0) {
aState.prevMaxNegBottomMargin = -bottomMargin;
} else {
aState.prevMaxPosBottomMargin = bottomMargin;
}
childCount++;
// Remember where we just were in case we end up pushing children
prevKidFrame = kidFrame;
// Update mLastContentIsComplete now that this kid fits
mLastContentIsComplete = NS_FRAME_IS_COMPLETE(status);
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
// No, the child isn't complete
nsIFrame* kidNextInFlow;
kidFrame->GetNextInFlow(kidNextInFlow);
PRBool lastContentIsComplete = mLastContentIsComplete;
if (nsnull == kidNextInFlow) {
// The child doesn't have a next-in-flow so create a continuing
// frame. This hooks the child into the flow
nsIFrame* continuingFrame;
nsIStyleContext* kidSC;
kidFrame->GetStyleContext(aPresContext, kidSC);
kidFrame->CreateContinuingFrame(aPresContext, this, kidSC,
continuingFrame);
NS_RELEASE(kidSC);
NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
// Add the continuing frame to the sibling list
nsIFrame* nextSib;
kidFrame->GetNextSibling(nextSib);
continuingFrame->SetNextSibling(nextSib);
kidFrame->SetNextSibling(continuingFrame);
if (nsnull == nextSib) {
// Assume that the continuation frame we just created is
// complete, for now. It will get reflowed by our
// next-in-flow (we are going to push it now)
lastContentIsComplete = PR_TRUE;
}
}
// We've used up all of our available space so push the remaining
// children to the next-in-flow
nsIFrame* nextSibling;
kidFrame->GetNextSibling(nextSibling);
if (nsnull != nextSibling) {
PushChildren(nextSibling, kidFrame, lastContentIsComplete);
/* debug */
/*
printf ("having just pushed children, here is the frame hierarchy.\n");
nsIFrame *p;
GetGeometricParent(p);
nsIFrame *root;
while (nsnull!=p)
{
root = p;
p->GetGeometricParent(p);
}
root->List();
fflush(stdout);
*/
/* end debug */
SetLastContentOffset(prevKidFrame);
}
result = PR_FALSE;
break;
}
// Get the next child
kidFrame->GetNextSibling(kidFrame);
// XXX talk with troy about checking for available space here
}
// Update the child count
mChildCount = childCount;
NS_POSTCONDITION(LengthOf(mFirstChild) == mChildCount, "bad child count");
// Set the last content offset based on the last child we mapped.
NS_ASSERTION(IsLastChild(prevKidFrame), "unexpected last child");
SetLastContentOffset(prevKidFrame);
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
#ifdef NOISY
ListTag(stdout);
printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
(result ? "" : "NOT "),
mChildCount,
mFirstContentOffset, mLastContentOffset,
(mLastContentIsComplete ? 'T' : 'F'));
#ifdef NOISY_FLOW
{
nsTableOuterFrame* flow = (nsTableOuterFrame*) mNextInFlow;
while (flow != 0) {
printf(" %p: [%d,%d,%c]\n",
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
(flow->mLastContentIsComplete ? 'T' : 'F'));
flow = (nsTableOuterFrame*) flow->mNextInFlow;
}
}
#endif
#endif
return result;
}
/**
* Try and pull-up frames from our next-in-flow
*
* @param aPresContext presentation context to use
* @param aState current inline state
* @return true if we successfully pulled-up all the children and false
* otherwise, e.g. child didn't fit
*/
PRBool nsTableOuterFrame::PullUpChildren( nsIPresContext* aPresContext,
OuterTableReflowState& aState,
nsSize* aMaxElementSize)
{
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
#ifdef NOISY
ListTag(stdout);
printf(": pullup (childCount=%d) [%d,%d,%c]\n",
mChildCount,
mFirstContentOffset, mLastContentOffset,
(mLastContentIsComplete ? 'T' : 'F'));
#ifdef NOISY_FLOW
{
nsTableOuterFrame* flow = (nsTableOuterFrame*) mNextInFlow;
while (flow != 0) {
printf(" %p: [%d,%d,%c]\n",
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
(flow->mLastContentIsComplete ? 'T' : 'F'));
flow = (nsTableOuterFrame*) flow->mNextInFlow;
}
}
#endif
#endif
nsTableOuterFrame* nextInFlow = (nsTableOuterFrame*)mNextInFlow;
nsSize kidMaxElementSize(0,0);
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
#ifdef NS_DEBUG
PRInt32 kidIndex = NextChildOffset();
#endif
nsIFrame* prevKidFrame;
LastChild(prevKidFrame);
// This will hold the prevKidFrame's mLastContentIsComplete
// status. If we have to push the frame that follows prevKidFrame
// then this will become our mLastContentIsComplete state. Since
// prevKidFrame is initially our last frame, it's completion status
// is our mLastContentIsComplete value.
PRBool prevLastContentIsComplete = mLastContentIsComplete;
PRBool result = PR_TRUE;
while (nsnull != nextInFlow) {
nsReflowMetrics kidSize(pKidMaxElementSize);
kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0;
nsReflowStatus status;
// Get the next child
nsIFrame* kidFrame = nextInFlow->mFirstChild;
// Any more child frames?
if (nsnull == kidFrame) {
// No. Any frames on its overflow list?
if (nsnull != nextInFlow->mOverflowList) {
// Move the overflow list to become the child list
//NS_ABORT();
nextInFlow->AppendChildren(nextInFlow->mOverflowList);
nextInFlow->mOverflowList = nsnull;
kidFrame = nextInFlow->mFirstChild;
} else {
// We've pulled up all the children, so move to the next-in-flow.
nextInFlow->GetNextInFlow((nsIFrame*&)nextInFlow);
continue;
}
}
// See if the child fits in the available space. If it fits or
// it's splittable then reflow it. The reason we can't just move
// it is that we still need ascent/descent information
nsSize kidFrameSize(0,0);
nsSplittableType kidIsSplittable;
kidFrame->GetSize(kidFrameSize);
kidFrame->IsSplittable(kidIsSplittable);
if ((kidFrameSize.width > aState.availSize.height) &&
NS_FRAME_IS_NOT_SPLITTABLE(kidIsSplittable)) {
result = PR_FALSE;
mLastContentIsComplete = prevLastContentIsComplete;
break;
}
kidFrame->WillReflow(*aPresContext);
nsReflowState kidReflowState(kidFrame, aState.reflowState, kidFrame == mInnerTableFrame ?
aState.innerTableMaxSize : aState.availSize);
status = ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState,
pKidMaxElementSize, aState);
// Did the child fit?
if ((kidSize.height > aState.availSize.height) && (nsnull != mFirstChild)) {
// The child is too wide to fit in the available space, and it's
// not our first child
result = PR_FALSE;
mLastContentIsComplete = prevLastContentIsComplete;
break;
}
// Place the child after taking into account it's margin
// aState.y += topMargin;
nsRect kidRect (0, 0, kidSize.width, kidSize.height);
// kidRect.x += kidMol->margin.left;
kidRect.y += aState.y;
PlaceChild(aState, kidFrame, kidRect, aMaxElementSize, *pKidMaxElementSize);
// Remove the frame from its current parent
kidFrame->GetNextSibling(nextInFlow->mFirstChild);
nextInFlow->mChildCount--;
// Update the next-in-flows first content offset
if (nsnull != nextInFlow->mFirstChild) {
nextInFlow->SetFirstContentOffset(nextInFlow->mFirstChild);
}
// Link the frame into our list of children
kidFrame->SetGeometricParent(this);
nsIFrame* kidContentParent;
kidFrame->GetContentParent(kidContentParent);
if (nextInFlow == kidContentParent) {
kidFrame->SetContentParent(this);
}
if (nsnull == prevKidFrame) {
mFirstChild = kidFrame;
SetFirstContentOffset(kidFrame);
} else {
prevKidFrame->SetNextSibling(kidFrame);
}
kidFrame->SetNextSibling(nsnull);
mChildCount++;
// Remember where we just were in case we end up pushing children
prevKidFrame = kidFrame;
prevLastContentIsComplete = mLastContentIsComplete;
// Is the child we just pulled up complete?
mLastContentIsComplete = NS_FRAME_IS_COMPLETE(status);
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
// No the child isn't complete
nsIFrame* kidNextInFlow;
kidFrame->GetNextInFlow(kidNextInFlow);
if (nsnull == kidNextInFlow) {
// The child doesn't have a next-in-flow so create a
// continuing frame. The creation appends it to the flow and
// prepares it for reflow.
nsIFrame* continuingFrame;
nsIStyleContext* kidSC;
kidFrame->GetStyleContext(aPresContext, kidSC);
kidFrame->CreateContinuingFrame(aPresContext, this, kidSC,
continuingFrame);
NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
// Add the continuing frame to our sibling list and then push
// it to the next-in-flow. This ensures the next-in-flow's
// content offsets and child count are set properly. Note that
// we can safely assume that the continuation is complete so
// we pass PR_TRUE into PushChidren in case our next-in-flow
// was just drained and now needs to know it's
// mLastContentIsComplete state.
kidFrame->SetNextSibling(continuingFrame);
PushChildren(continuingFrame, kidFrame, PR_TRUE);
// After we push the continuation frame we don't need to fuss
// with mLastContentIsComplete beause the continuation frame
// is no longer on *our* list.
}
// If the child isn't complete then it means that we've used up
// all of our available space.
result = PR_FALSE;
break;
}
}
// Update our last content offset
if (nsnull != prevKidFrame) {
NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
SetLastContentOffset(prevKidFrame);
}
// We need to make sure the first content offset is correct for any empty
// next-in-flow frames (frames where we pulled up all the child frames)
nextInFlow = (nsTableOuterFrame*)mNextInFlow;
if ((nsnull != nextInFlow) && (nsnull == nextInFlow->mFirstChild)) {
// We have at least one empty frame. Did we succesfully pull up all the
// child frames?
if (PR_FALSE == result) {
// No, so we need to adjust the first content offset of all the empty
// frames
AdjustOffsetOfEmptyNextInFlows();
#ifdef NS_DEBUG
} else {
// Yes, we successfully pulled up all the child frames which means all
// the next-in-flows must be empty. Do a sanity check
while (nsnull != nextInFlow) {
NS_ASSERTION(nsnull == nextInFlow->mFirstChild, "non-empty next-in-flow");
nextInFlow->GetNextInFlow((nsIFrame*&)nextInFlow);
}
#endif
}
}
#ifdef NS_DEBUG
PRInt32 len = LengthOf(mFirstChild);
NS_ASSERTION(len == mChildCount, "bad child count");
VerifyLastIsComplete();
#endif
#ifdef NOISY
ListTag(stdout);
printf(": pullup %sok (childCount=%d) [%d,%d,%c]\n",
(result ? "" : "NOT "),
mChildCount,
mFirstContentOffset, mLastContentOffset,
(mLastContentIsComplete ? 'T' : 'F'));
#ifdef NOISY_FLOW
{
nsTableOuterFrame* flow = (nsTableOuterFrame*) mNextInFlow;
while (flow != 0) {
printf(" %p: [%d,%d,%c]\n",
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
(flow->mLastContentIsComplete ? 'T' : 'F'));
flow = (nsTableOuterFrame*) flow->mNextInFlow;
}
}
#endif
#endif
return result;
}
/** set the OuterTableReflowState based on the child frame we are currently processing.
* must be called before ReflowChild, at a minimum.
*/
void nsTableOuterFrame::SetReflowState(OuterTableReflowState& aState, nsIFrame* aKidFrame)
{
nsIContentPtr kid;
aKidFrame->GetContent(kid.AssignRef()); // kid: REFCNT++
nsITableContent *tableContentInterface = nsnull;
kid->QueryInterface(kITableContentIID, (void**)&tableContentInterface);// tableContentInterface: REFCNT++
if (nsnull!=tableContentInterface)
{
aState.processingCaption = PR_TRUE;
NS_RELEASE(tableContentInterface); // tableContentInterface: REFCNT--
}
else
aState.processingCaption = PR_FALSE;
}
/**
* Reflow a child frame and return the status of the reflow. If the
* child is complete and it has next-in-flows (it was a splittable child)
* then delete the next-in-flows.
*/
nsReflowStatus
nsTableOuterFrame::ReflowChild( nsIFrame* aKidFrame,
nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aKidReflowState,
nsSize* aMaxElementSize,
OuterTableReflowState& aState)
{
nsReflowStatus status;
/* call the appropriate reflow method based on the type and position of the child */
if (PR_TRUE==aState.processingCaption)
{ // it's a caption, find out if it's top or bottom
// Resolve style
#if 0
const nsStyleText* captionStyle;
aKidFrame->GetStyleData(eStyleStruct_Text, ( nsStyleStruct *&)captionStyle);
NS_ASSERTION(nsnull != captionStyle, "null style molecule for caption");
if ((eStyleUnit_Enumerated == captionStyle->mVerticalAlign.GetUnit()) &&
(NS_STYLE_VERTICAL_ALIGN_BOTTOM==captionStyle->mVerticalAlign.GetIntValue()))
{
if (PR_TRUE==gsDebug) printf("reflowChild called with a bottom caption\n");
status = ResizeReflowBottomCaptionsPass2(aPresContext, aDesiredSize,
aMaxSize, aMaxElementSize,
aState.y, aState);
}
else
{
if (PR_TRUE==gsDebug) printf("reflowChild called with a top caption\n");
status = ResizeReflowTopCaptionsPass2(aPresContext, aDesiredSize,
aMaxSize, aMaxElementSize, aState);
}
#else
status = nsContainerFrame::ReflowChild(aKidFrame, aPresContext, aDesiredSize,
aKidReflowState);
#endif
}
else
{
if (PR_TRUE==gsDebug) printf("reflowChild called with a table body\n");
if (eReflowReason_Incremental == aKidReflowState.reason) {
aKidFrame->Reflow(aPresContext, aDesiredSize, aKidReflowState, status);
} else {
status = ((nsTableFrame*)aKidFrame)->ResizeReflowPass2(aPresContext, aDesiredSize, aKidReflowState,
mMinCaptionWidth, mMaxCaptionWidth);
}
}
if (PR_TRUE==gsDebug)
{
}
if (NS_FRAME_IS_COMPLETE(status)) {
nsIFrame* kidNextInFlow;
aKidFrame->GetNextInFlow(kidNextInFlow);
if (nsnull != kidNextInFlow) {
// Remove all of the childs next-in-flows. Make sure that we ask
// the right parent to do the removal (it's possible that the
// parent is not this because we are executing pullup code)
nsTableOuterFrame* parent;
aKidFrame->GetGeometricParent((nsIFrame*&)parent);
parent->DeleteChildsNextInFlow(aKidFrame);
}
}
return status;
}
void nsTableOuterFrame::CreateChildFrames(nsIPresContext* aPresContext)
{
nsIFrame *prevKidFrame = nsnull;
nsresult frameCreated = nsTableFrame::NewFrame((nsIFrame **)(&mInnerTableFrame), mContent, this);
if (NS_OK!=frameCreated)
return; // SEC: an error!!!!
// Resolve style
nsIStyleContextPtr kidStyleContext =
aPresContext->ResolveStyleContextFor(mContent, this);
NS_ASSERTION(kidStyleContext.IsNotNull(), "bad style context for kid.");
mInnerTableFrame->SetStyleContext(aPresContext,kidStyleContext);
mChildCount++;
// Link child frame into the list of children
mFirstChild = mInnerTableFrame;
prevKidFrame = mInnerTableFrame;
// now create the caption frames, prepending top captions and
// appending bottom captions
mCaptionFrames = new nsVoidArray();
// create caption frames as needed
nsIFrame *lastTopCaption = nsnull;
for (PRInt32 kidIndex=0; /* nada */ ;kidIndex++)
{
nsIContentPtr caption = mContent->ChildAt(kidIndex);
if (PR_TRUE==caption.IsNull()) {
break;
}
// Resolve style
nsIStyleContextPtr captionSC =
aPresContext->ResolveStyleContextFor(caption, this);
NS_ASSERTION(captionSC.IsNotNull(), "bad style context for caption.");
nsStyleDisplay *childDisplay = (nsStyleDisplay*)captionSC->GetStyleData(eStyleStruct_Display);
if (NS_STYLE_DISPLAY_TABLE_CAPTION == childDisplay->mDisplay)
{
const nsStyleText* captionTextStyle =
(const nsStyleText*)captionSC->GetStyleData(eStyleStruct_Text);
// create the frame
nsIFrame *captionFrame=nsnull;
frameCreated = ((nsIHTMLContent*)(nsIContent*)caption)->CreateFrame(aPresContext,
this, captionSC, captionFrame);
if (NS_OK!=frameCreated)
return; // SEC: an error!!!!
mChildCount++;
// Link child frame into the list of children
if ((eStyleUnit_Enumerated == captionTextStyle->mVerticalAlign.GetUnit()) &&
(NS_STYLE_VERTICAL_ALIGN_BOTTOM==captionTextStyle->mVerticalAlign.GetIntValue()))
{ // bottom captions get added to the end of the outer frame child list
prevKidFrame->SetNextSibling(captionFrame);
prevKidFrame = captionFrame;
// bottom captions get remembered in an instance variable for easy access later
if (nsnull==mBottomCaptions)
{
mBottomCaptions = new nsVoidArray();
}
mBottomCaptions->AppendElement(captionFrame);
}
else
{ // top captions get prepended to the outer frame child list
if (nsnull == lastTopCaption)
{ // our first top caption, therefore our first child
mFirstChild = captionFrame;
mFirstChild->SetNextSibling(mInnerTableFrame);
}
else
{ // just another grub in the brood of top captions
nsIFrame* nextSibling;
lastTopCaption->GetNextSibling(nextSibling);
captionFrame->SetNextSibling(nextSibling);
lastTopCaption->SetNextSibling(captionFrame);
lastTopCaption = captionFrame;
}
lastTopCaption = captionFrame;
}
mCaptionFrames->AppendElement(captionFrame);
}
else
{ // otherwise I know there are no more captions
// I'm assuming the frames were created in the correct order
break;
}
}
}
nsReflowStatus
nsTableOuterFrame::ResizeReflowCaptionsPass1(nsIPresContext* aPresContext,
OuterTableReflowState& aState,
PRBool aIsInitialReflow)
{
if (nsnull!=mCaptionFrames)
{
PRInt32 numCaptions = mCaptionFrames->Count();
nsReflowReason reflowReason = aIsInitialReflow ? eReflowReason_Initial :
eReflowReason_Resize;
for (PRInt32 captionIndex = 0; captionIndex < numCaptions; captionIndex++)
{
nsSize maxElementSize(0,0);
nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
nsReflowMetrics kidSize(&maxElementSize);
kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0;
nsIFrame *captionFrame = (nsIFrame*)mCaptionFrames->ElementAt(captionIndex);
nsReflowStatus status;
nsReflowState reflowState(captionFrame, aState.reflowState, maxSize,
reflowReason);
captionFrame->WillReflow(*aPresContext);
captionFrame->Reflow(aPresContext, kidSize, reflowState, status);
if (mMinCaptionWidth<maxElementSize.width)
mMinCaptionWidth = maxElementSize.width;
if (mMaxCaptionWidth<kidSize.width)
mMaxCaptionWidth = kidSize.width;
// captionFrame->VerticallyAlignChild(aPresContext);
}
}
return NS_FRAME_COMPLETE;
}
#if 0
nsReflowStatus
nsTableOuterFrame::ResizeReflowTopCaptionsPass2(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsSize& aMaxSize,
nsSize* aMaxElementSize,
OuterTableReflowState& aState)
{
nsReflowStatus result = NS_FRAME_COMPLETE;
nscoord topCaptionY = 0;
if (nsnull!=mCaptionFrames)
{
PRInt32 numCaptions = mCaptionFrames->Count();
for (PRInt32 captionIndex = 0; captionIndex < numCaptions; captionIndex++)
{
// get the caption
nsIFrame* captionFrame = (nsIFrame*)mCaptionFrames->ElementAt(captionIndex);
// Resolve style
const nsStyleText* captionTextStyle;
captionFrame->GetStyleData(eStyleStruct_Text, (nsStyleStruct *&)captionTextStyle);
NS_ASSERTION(nsnull != captionTextStyle, "null style molecule for caption");
if ((eStyleUnit_Enumerated == captionTextStyle->mVerticalAlign.GetUnit()) &&
(NS_STYLE_VERTICAL_ALIGN_BOTTOM==captionTextStyle->mVerticalAlign.GetIntValue()))
{
}
else
{ // top-align captions, the default
// reflow the caption, skipping top captions after the first that doesn't fit
if (NS_FRAME_IS_COMPLETE(result))
{
nsReflowMetrics desiredSize(nsnull);
desiredSize.width=desiredSize.height=desiredSize.ascent=desiredSize.descent=0;
nsReflowState reflowState(captionFrame, aState.reflowState, aMaxSize, eReflowReason_Resize);
captionFrame->WillReflow(*aPresContext);
result = nsContainerFrame::ReflowChild(captionFrame, aPresContext, desiredSize, reflowState);
// place the caption
captionFrame->SetRect(nsRect(0, topCaptionY, desiredSize.width, desiredSize.height));
if (NS_UNCONSTRAINEDSIZE!=desiredSize.height)
topCaptionY += desiredSize.height;
else
topCaptionY = NS_UNCONSTRAINEDSIZE;
mInnerTableFrame->MoveTo(0, topCaptionY);
PRInt32 captionIndexInParent;
captionFrame->GetContentIndex(captionIndexInParent);
if (0==captionIndexInParent)
{
SetFirstContentOffset(captionFrame);
if (gsDebug) printf("OUTER: set first content offset to %d\n", GetFirstContentOffset()); //@@@
}
}
// else continue on, so we can build the mBottomCaptions list
}
}
}
aDesiredSize.width = aMaxSize.width;
aDesiredSize.height = topCaptionY;
if (nsnull!=aMaxElementSize)
{ // SEC: this isn't right
aMaxElementSize->width = aMaxSize.width;
aMaxElementSize->height = topCaptionY;
}
return result;
}
nsReflowStatus
nsTableOuterFrame::ResizeReflowBottomCaptionsPass2(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsSize& aMaxSize,
nsSize* aMaxElementSize,
nscoord aYOffset,
OuterTableReflowState& aState)
{
nsReflowStatus result = NS_FRAME_COMPLETE;
nscoord bottomCaptionY = aYOffset;
// for now, assume all captions are stacked vertically
if (nsnull!=mBottomCaptions)
{
PRInt32 numCaptions = mBottomCaptions->Count();
for (PRInt32 captionIndex = 0; captionIndex < numCaptions; captionIndex++)
{
// get the caption
nsIFrame* captionFrame = (nsIFrame*)mBottomCaptions->ElementAt(captionIndex);
// Resolve style
/*
nsIStyleContextPtr captionSC = captionFrame->GetStyleContext(aPresContext);
NS_ASSERTION(nsnull != captionSC, "null style context for caption");
nsStyleMolecule* captionStyle =
(nsStyleMolecule*)captionSC->GetData(eStyleStruct_Molecule);
NS_ASSERTION(nsnull != captionStyle, "null style molecule for caption");
*/
// reflow the caption
nsReflowMetrics desiredSize(nsnull);
desiredSize.width=desiredSize.height=desiredSize.ascent=desiredSize.descent=0;
nsReflowState reflowState(captionFrame, aState.reflowState, aMaxSize, eReflowReason_Resize);
captionFrame->WillReflow(*aPresContext);
result = nsContainerFrame::ReflowChild(captionFrame, aPresContext, desiredSize, reflowState);
// place the caption
nsRect rect;
captionFrame->GetRect(rect);
rect.y = bottomCaptionY;
rect.width=desiredSize.width;
rect.height=desiredSize.height;
captionFrame->SetRect(rect);
if (NS_UNCONSTRAINEDSIZE!=desiredSize.height)
bottomCaptionY += desiredSize.height;
else
bottomCaptionY = NS_UNCONSTRAINEDSIZE;
}
}
aDesiredSize.width = aMaxSize.width;
aDesiredSize.height = bottomCaptionY-aYOffset;
if (nsnull!=aMaxElementSize)
{ // SEC: not right
aMaxElementSize->width = aMaxSize.width;
aMaxElementSize->height = bottomCaptionY-aYOffset;
}
return result;
}
#endif
/**
* Sets the last content offset based on the last child frame. If the last
* child is a pseudo frame then it sets mLastContentIsComplete to be the same
* as the last child's mLastContentIsComplete
*/
/*
void nsTableOuterFrame::SetLastContentOffset(const nsIFrame* aLastChild)
{
NS_PRECONDITION(nsnull != aLastChild, "bad argument");
NS_PRECONDITION(aLastChild->GetGeometricParent() == this, "bad geometric parent");
if (ChildIsPseudoFrame(aLastChild)) {
nsContainerFrame* pseudoFrame = (nsContainerFrame*)aLastChild;
mLastContentOffset = pseudoFrame->GetLastContentOffset();
mLastContentIsComplete = pseudoFrame->GetLastContentIsComplete();
} else {
mLastContentOffset = aLastChild->GetIndexInParent();
}
#ifdef NS_DEBUG
if (mLastContentOffset < mFirstContentOffset) {
nsIFrame* top = this;
while (top->GetGeometricParent() != nsnull) {
top = top->GetGeometricParent();
}
top->List();
}
#endif
// it is this next line that forces us to re-implement this method here
//NS_ASSERTION(mLastContentOffset >= mFirstContentOffset, "unexpected content mapping");
}
*/
NS_METHOD
nsTableOuterFrame::CreateContinuingFrame(nsIPresContext* aPresContext,
nsIFrame* aParent,
nsIStyleContext* aStyleContext,
nsIFrame*& aContinuingFrame)
{
nsTableOuterFrame* cf = new nsTableOuterFrame(mContent, aParent);
if (nsnull == cf) {
return NS_ERROR_OUT_OF_MEMORY;
}
PrepareContinuingFrame(aPresContext, aParent, aStyleContext, cf);
if (PR_TRUE==gsDebug)
printf("nsTableOuterFrame::CCF parent = %p, this=%p, cf=%p\n", aParent, this, cf);
aContinuingFrame = cf;
return NS_OK;
}
void
nsTableOuterFrame::PrepareContinuingFrame(nsIPresContext* aPresContext,
nsIFrame* aParent,
nsIStyleContext* aStyleContext,
nsTableOuterFrame* aContFrame)
{
// Append the continuing frame to the flow
aContFrame->AppendToFlow(this);
// Initialize it's content offsets. Note that we assume for now that
// the continuingFrame will map the remainder of the content and
// that therefore mLastContentIsComplete will be true.
PRInt32 nextOffset;
if (mChildCount > 0) {
nextOffset = mLastContentOffset;
if (mLastContentIsComplete) {
nextOffset++;
}
} else {
nextOffset = mFirstContentOffset;
}
aContFrame->SetFirstContentOffset(nextOffset);
aContFrame->SetLastContentOffset(nextOffset);
aContFrame->SetLastContentIsComplete(PR_TRUE);
aContFrame->SetStyleContext(aPresContext, aStyleContext);
}
NS_METHOD nsTableOuterFrame::VerifyTree() const
{
#ifdef NS_DEBUG
#endif
return NS_OK;
}
/**
* Remove and delete aChild's next-in-flow(s). Updates the sibling and flow
* pointers.
*
* Updates the child count and content offsets of all containers that are
* affected
*
* Overloaded here because nsContainerFrame makes assumptions about pseudo-frames
* that are not true for tables.
*
* @param aChild child this child's next-in-flow
* @return PR_TRUE if successful and PR_FALSE otherwise
*/
PRBool nsTableOuterFrame::DeleteChildsNextInFlow(nsIFrame* aChild)
{
NS_PRECONDITION(IsChild(aChild), "bad geometric parent");
nsIFrame* nextInFlow;
aChild->GetNextInFlow(nextInFlow);
NS_PRECONDITION(nsnull != nextInFlow, "null next-in-flow");
nsTableOuterFrame* parent;
nextInFlow->GetGeometricParent((nsIFrame*&)parent);
// If the next-in-flow has a next-in-flow then delete it too (and
// delete it first).
nsIFrame* nextNextInFlow;
nextInFlow->GetNextInFlow(nextNextInFlow);
if (nsnull != nextNextInFlow) {
parent->DeleteChildsNextInFlow(nextInFlow);
}
#ifdef NS_DEBUG
PRInt32 childCount;
nsIFrame* firstChild;
nextInFlow->ChildCount(childCount);
nextInFlow->FirstChild(firstChild);
NS_ASSERTION(childCount == 0, "deleting !empty next-in-flow");
NS_ASSERTION((0 == childCount) && (nsnull == firstChild), "deleting !empty next-in-flow");
#endif
// Disconnect the next-in-flow from the flow list
nextInFlow->BreakFromPrevFlow();
// Take the next-in-flow out of the parent's child list
if (parent->mFirstChild == nextInFlow) {
nextInFlow->GetNextSibling(parent->mFirstChild);
if (nsnull != parent->mFirstChild) {
parent->SetFirstContentOffset(parent->mFirstChild);
if (parent->IsPseudoFrame()) {
// Tell the parent's parent to update its content offsets
nsContainerFrame* pp = (nsContainerFrame*) parent->mGeometricParent;
pp->PropagateContentOffsets(parent, parent->mFirstContentOffset,
parent->mLastContentOffset,
parent->mLastContentIsComplete);
}
}
// When a parent loses it's last child and that last child is a
// pseudo-frame then the parent's content offsets are now wrong.
// However, we know that the parent will eventually be reflowed
// in one of two ways: it will either get a chance to pullup
// children or it will be deleted because it's prev-in-flow
// (e.g. this) is complete. In either case, the content offsets
// will be repaired.
} else {
nsIFrame* nextSibling;
// Because the next-in-flow is not the first child of the parent
// we know that it shares a parent with aChild. Therefore, we need
// to capture the next-in-flow's next sibling (in case the
// next-in-flow is the last next-in-flow for aChild AND the
// next-in-flow is not the last child in parent)
NS_ASSERTION(parent->IsChild(aChild), "screwy flow");
aChild->GetNextSibling(nextSibling);
NS_ASSERTION(nextSibling == nextInFlow, "unexpected sibling");
nextInFlow->GetNextSibling(nextSibling);
aChild->SetNextSibling(nextSibling);
}
// Delete the next-in-flow frame and adjust it's parent's child count
nextInFlow->DeleteFrame();
parent->mChildCount--;
#ifdef NS_DEBUG
aChild->GetNextInFlow(nextInFlow);
NS_POSTCONDITION(nsnull == nextInFlow, "non null next-in-flow");
#endif
return PR_TRUE;
}
void nsTableOuterFrame::CreateInnerTableFrame(nsIPresContext* aPresContext)
{
// Do we have a prev-in-flow?
if (nsnull == mPrevInFlow) {
// No, create a column pseudo frame
mInnerTableFrame = new nsTableFrame(mContent, this);
mChildCount++;
// Resolve style and set the style context
nsIStyleContextPtr styleContext =
aPresContext->ResolveStyleContextFor(mContent, this);
mInnerTableFrame->SetStyleContext(aPresContext,styleContext);
} else {
nsTableOuterFrame* prevOuterTable = (nsTableOuterFrame*)mPrevInFlow;
nsIFrame* prevInnerTable = prevOuterTable->mInnerTableFrame;
// Create a continuing column
prevInnerTable->GetNextInFlow((nsIFrame*&)mInnerTableFrame);
if (nsnull==mInnerTableFrame)
{
nsIFrame* continuingFrame;
nsIStyleContext* kidSC;
prevInnerTable->GetStyleContext(aPresContext, kidSC);
prevInnerTable->CreateContinuingFrame(aPresContext, this, kidSC,
continuingFrame);
mInnerTableFrame = (nsTableFrame*)continuingFrame;
mChildCount++;
}
}
}
/* ----- static methods ----- */
nsresult nsTableOuterFrame::NewFrame(nsIFrame** aInstancePtrResult,
nsIContent* aContent,
nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
nsIFrame* it = new nsTableOuterFrame(aContent, aParent);
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aInstancePtrResult = it;
return NS_OK;
}