diff --git a/mozilla/layout/html/table/src/nsTableCellFrame.cpp b/mozilla/layout/html/table/src/nsTableCellFrame.cpp index ca74d55ba83..736326dbbc3 100644 --- a/mozilla/layout/html/table/src/nsTableCellFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableCellFrame.cpp @@ -145,6 +145,30 @@ PRInt32 nsTableCellFrame::GetRowSpan() return result; } +/** helper method to get the col span of this frame's content (which must be a cell) */ +PRInt32 nsTableCellFrame::GetColSpan() +{ + PRInt32 result = 0; + nsTableCell *cellContent = (nsTableCell *)mContent; + if (nsnull!=cellContent) + { + result = cellContent->GetColSpan(); + } + return result; +} + +/** helper method to get the col index of this frame's content (which must be a cell) */ +PRInt32 nsTableCellFrame::GetColIndex() +{ + PRInt32 result = 0; + nsTableCell *cellContent = (nsTableCell *)mContent; + if (nsnull!=cellContent) + { + result = cellContent->GetColIndex(); + } + return result; +} + void nsTableCellFrame::CreatePsuedoFrame(nsIPresContext* aPresContext) { // Do we have a prev-in-flow? @@ -229,6 +253,8 @@ NS_METHOD nsTableCellFrame::ResizeReflow(nsIPresContext* aPresContext, // Try to reflow the child into the available space. It might not // fit or might need continuing. + if (availSize.height < 0) + availSize.height = 1; nsSize maxKidElementSize; if (gsDebug==PR_TRUE) printf(" nsTableCellFrame::ResizeReflow calling ReflowChild with availSize=%d,%d\n", diff --git a/mozilla/layout/html/table/src/nsTableCellFrame.h b/mozilla/layout/html/table/src/nsTableCellFrame.h index 3199b6efa5d..65daa2c299a 100644 --- a/mozilla/layout/html/table/src/nsTableCellFrame.h +++ b/mozilla/layout/html/table/src/nsTableCellFrame.h @@ -63,8 +63,15 @@ public: void VerticallyAlignChild(nsIPresContext* aPresContext); + /** return the mapped cell's row span. Always >= 1. */ virtual PRInt32 GetRowSpan(); + /** return the mapped cell's col span. Always >= 1. */ + virtual PRInt32 GetColSpan(); + + /** return the mapped cell's column index (starting at 0 for the first column) */ + virtual PRInt32 GetColIndex(); + virtual ~nsTableCellFrame(); protected: diff --git a/mozilla/layout/html/table/src/nsTableFrame.cpp b/mozilla/layout/html/table/src/nsTableFrame.cpp index e4de2748789..f34bd8818ce 100644 --- a/mozilla/layout/html/table/src/nsTableFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableFrame.cpp @@ -90,6 +90,12 @@ struct InnerTableReflowState { // Flag for whether we're dealing with the first interior row group PRBool firstRowGroup; + // a list of the footers in this table frame, for quick access when inserting bodies + nsVoidArray *footerList; + + // cache the total height of the footers for placing body rows + nscoord footerHeight; + InnerTableReflowState(nsIPresContext* aPresContext, const nsSize& aMaxSize, nsStyleMolecule* aMol) @@ -103,9 +109,13 @@ struct InnerTableReflowState { unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); firstRowGroup = PR_TRUE; + footerHeight = 0; + footerList = nsnull; } ~InnerTableReflowState() { + if (nsnull!=footerList) + delete footerList; } }; @@ -712,7 +722,7 @@ nsIFrame::ReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresCont nsIFrame* prevKidFrame = nsnull;/* XXX incremental reflow! */ #ifdef NS_DEBUG - PreReflowCheck(); + //PreReflowCheck(); #endif // Initialize out parameter @@ -775,7 +785,7 @@ nsIFrame::ReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresCont } // Return our desired rect - NS_ASSERTION(0GetRowGroupType(kidType); + if (tFootTag==kidType) + { + if (nsnull==aState.footerList) + aState.footerList = new nsVoidArray(); + aState.footerList->AppendElement((void *)aKidFrame); + aState.footerHeight += aKidRect.height; + } + // else if this is a body row group, push down all the footer row groups + else + { + // don't bother unless there are footers to push down + if (nsnull!=aState.footerList && 0!=aState.footerList->Count()) + { + nsPoint origin; + aKidFrame->GetOrigin(origin); + origin.y -= aState.footerHeight; + aKidFrame->MoveTo(origin.x, origin.y); + nsIAtom * tBodyTag = NS_NewAtom(nsTablePart::kRowGroupBodyTagString); // tBodyTag: REFCNT++ + if (tBodyTag==kidType) + { + PRInt32 numFooters = aState.footerList->Count(); + for (PRInt32 footerIndex = 0; footerIndex < numFooters; footerIndex++) + { + nsTableRowGroupFrame * footer = (nsTableRowGroupFrame *)(aState.footerList->ElementAt(footerIndex)); + NS_ASSERTION(nsnull!=footer, "bad footer list in table inner frame."); + if (nsnull!=footer) + { + footer->GetOrigin(origin); + origin.y += aKidRect.height; + footer->MoveTo(origin.x, origin.y); + } + } + } + NS_RELEASE(tBodyTag); + } + } + NS_RELEASE(tFootTag); + // Update the maximum element size if (PR_TRUE==aState.firstRowGroup) { @@ -1284,8 +1336,8 @@ PRBool nsTableFrame::PullUpChildren(nsIPresContext* aPresContext, */ nsIFrame::ReflowStatus nsTableFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, - InnerTableReflowState& aState, - nsSize* aMaxElementSize) + InnerTableReflowState& aState, + nsSize* aMaxElementSize) { #ifdef NS_DEBUG VerifyLastIsComplete(); @@ -2441,16 +2493,53 @@ NS_METHOD nsTableFrame::CreateContinuingFrame(nsIPresContext* aPresContext, cf->SetRect(nsRect(0, 0, mRect.width, 0)); // add headers and footers to cf nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow(); - PRInt32 childCount; - - firstInFlow->ChildCount(childCount); - PRInt32 childIndex = 0; - for (; childIndex < childCount; childIndex++) + nsIFrame * rg = nsnull; + firstInFlow->ChildAt(0, rg); + NS_ASSERTION (nsnull!=rg, "previous frame has no children"); + nsIAtom * tHeadTag = NS_NewAtom(nsTablePart::kRowGroupHeadTagString); // tHeadTag: REFCNT++ + nsIAtom * tFootTag = NS_NewAtom(nsTablePart::kRowGroupFootTagString); // tFootTag: REFCNT++ + PRInt32 index = 0; + nsIFrame * bodyRowGroupFromOverflow = mOverflowList; + nsIFrame * lastSib = nsnull; + for ( ; nsnull!=rg; index++) { - // TODO: place copies of the header and footer row groups here - // maybe need to do this in ResizeReflow at the beginning, when we determine we are a continuing frame + nsIContent *content = nsnull; + rg->GetContent(content); // content: REFCNT++ + NS_ASSERTION(nsnull!=content, "bad frame, returned null content."); + nsIAtom * rgTag = content->GetTag(); + // if we've found a header or a footer, replicate it + if (tHeadTag==rgTag || tFootTag==rgTag) + { + printf("found a head or foot in continuing frame\n"); + // Resolve style for the child + nsIStyleContext* kidStyleContext = + aPresContext->ResolveStyleContextFor(content, cf); // kidStyleContext: REFCNT++ + nsStyleMolecule* kidMol = + (nsStyleMolecule*)kidStyleContext->GetData(kStyleMoleculeSID); + nsIContentDelegate* kidDel = nsnull; + kidDel = content->GetDelegate(aPresContext); // kidDel: REFCNT++ + nsIFrame * duplicateFrame = kidDel->CreateFrame(aPresContext, content, index, cf); + NS_RELEASE(kidDel); // kidDel: REFCNT-- + duplicateFrame->SetStyleContext(kidStyleContext); + NS_RELEASE(kidStyleContext); // kidStyleContenxt: REFCNT-- + + if (nsnull==lastSib) + { + mOverflowList = duplicateFrame; + } + else + { + lastSib->SetNextSibling(duplicateFrame); + } + duplicateFrame->SetNextSibling(bodyRowGroupFromOverflow); + lastSib = duplicateFrame; + } + NS_RELEASE(content); // content: REFCNT-- + // get the next row group + rg->GetNextSibling(rg); } - + NS_RELEASE(tFootTag); // tHeadTag: REFCNT -- + NS_RELEASE(tHeadTag); // tFootTag: REFCNT -- aContinuingFrame = cf; return NS_OK; } diff --git a/mozilla/layout/html/table/src/nsTableOuterFrame.cpp b/mozilla/layout/html/table/src/nsTableOuterFrame.cpp index 2439865157f..a33de28ae1f 100644 --- a/mozilla/layout/html/table/src/nsTableOuterFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableOuterFrame.cpp @@ -305,7 +305,13 @@ NS_METHOD nsTableOuterFrame::ResizeReflow(nsIPresContext* aPresContext, //NS_ASSERTION(0 aState.availSize.height) && (kidFrame != mFirstChild)) { - // The child is too wide to fit in the available space, and it's + // 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 @@ -518,8 +524,6 @@ PRBool nsTableOuterFrame::ReflowMappedChildren( nsIPresContext* aPresContex // Update mLastContentIsComplete now that this kid fits mLastContentIsComplete = PRBool(status == frComplete); - // Is the child complete? - mLastContentIsComplete = PRBool(status == frComplete); if (frNotComplete == status) { // No, the child isn't complete nsIFrame* kidNextInFlow; @@ -555,6 +559,21 @@ PRBool nsTableOuterFrame::ReflowMappedChildren( nsIPresContext* aPresContex 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; diff --git a/mozilla/layout/html/table/src/nsTablePart.cpp b/mozilla/layout/html/table/src/nsTablePart.cpp index dfd8ccae9d4..80099be0d52 100644 --- a/mozilla/layout/html/table/src/nsTablePart.cpp +++ b/mozilla/layout/html/table/src/nsTablePart.cpp @@ -593,7 +593,7 @@ PRBool nsTablePart::AppendRowGroup (nsTableRowGroup *aContent) // if aContent is a body and the current child is a footer, stop, we've found the spot else if (tBodyTag==rowGroupTag && tFootTag==tableChildTag) { - break; + continue; } // if aContent is a body and we've gotten this far, keep going else if (tBodyTag==rowGroupTag) diff --git a/mozilla/layout/html/table/src/nsTableRowFrame.cpp b/mozilla/layout/html/table/src/nsTableRowFrame.cpp index 2af64ea1fb8..318e92d5cbb 100644 --- a/mozilla/layout/html/table/src/nsTableRowFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableRowFrame.cpp @@ -40,9 +40,61 @@ static const PRBool gsDebug2 = PR_FALSE; static NS_DEFINE_IID(kStyleMoleculeSID, NS_STYLEMOLECULE_SID); +/* ----------- RowReflowState ---------- */ + +struct RowReflowState { + + // The body's style molecule + nsStyleMolecule* mol; + + // The body's available size (computed from the body's parent) + nsSize availSize; + + // Margin tracking information + nscoord prevMaxPosBottomMargin; + nscoord prevMaxNegBottomMargin; + + // Flags for whether the max size is unconstrained + PRBool unconstrainedWidth; + PRBool unconstrainedHeight; + + // Running x-offset + nscoord x; + + // Height of tallest cell (excluding cells with rowspan>1) + nscoord maxCellHeight; + + nsTableFrame *tableFrame; + + + RowReflowState( nsIPresContext* aPresContext, + const nsSize& aMaxSize, + nsStyleMolecule* aMol) + { + mol = aMol; + availSize.width = aMaxSize.width; + availSize.height = aMaxSize.height; + prevMaxPosBottomMargin = 0; + prevMaxNegBottomMargin = 0; + x=0; + unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); + unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); + maxCellHeight=0; + tableFrame = nsnull; + } + + ~RowReflowState() { + } +}; + + + + +/* ----------- nsTableRowpFrame ---------- */ + nsTableRowFrame::nsTableRowFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParentFrame) + PRInt32 aIndexInParent, + nsIFrame* aParentFrame) : nsContainerFrame(aContent, aIndexInParent, aParentFrame), mTallestCell(0) { @@ -120,17 +172,759 @@ PRInt32 nsTableRowFrame::GetTallestChild() const return mTallestCell; } -/* +// Collapse child's top margin with previous bottom margin +nscoord nsTableRowFrame::GetTopMarginFor( nsIPresContext* aCX, + RowReflowState& aState, + nsStyleMolecule* aKidMol) +{ + nscoord margin; + nscoord maxNegTopMargin = 0; + nscoord maxPosTopMargin = 0; + if ((margin = aKidMol->margin.top) < 0) { + maxNegTopMargin = -margin; + } else { + maxPosTopMargin = margin; + } -want to use min space the child can fit in to -determine its real size, unless fixed columns sizes -are present. + 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 nsTableRowFrame::PlaceChild(nsIPresContext* aPresContext, + RowReflowState& aState, + nsIFrame* aKidFrame, + const nsRect& aKidRect, + nsSize* aMaxElementSize, + nsSize& aKidMaxElementSize) +{ + if (PR_TRUE==gsDebug1) + printf ("row: placing cell at %d, %d, %d, %d\n", + aKidRect.x, aKidRect.y, aKidRect.width, aKidRect.height); + + // Place and size the child + aKidFrame->SetRect(aKidRect); + + // Update the maximum element size + PRInt32 rowSpan = ((nsTableCellFrame*)aKidFrame)->GetRowSpan(); + if (nsnull != aMaxElementSize) { + aMaxElementSize->width += aKidMaxElementSize.width; + if ((1==rowSpan) && (aKidMaxElementSize.height>aMaxElementSize->height)) + { + aMaxElementSize->height = aKidMaxElementSize.height; + } + } + if ((1==rowSpan) && (aState.maxCellHeightmFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) 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 lastContentIsComplete = mLastContentIsComplete; + + nsSize kidMaxElementSize; + nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull; + PRBool result = PR_TRUE; + + for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) { + nsIContent* cell = nsnull; + kidFrame->GetContent(cell); // cell: REFCNT++ + nsSize kidAvailSize(aState.availSize); + if (0>=kidAvailSize.height) + kidAvailSize.height = 1; // XXX: HaCk - we don't handle negative heights yet + nsReflowMetrics desiredSize; + nsIFrame::ReflowStatus status; + + // Get top margin for this kid + nsIStyleContext* kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC); + nsStyleMolecule* kidMol = (nsStyleMolecule*)kidSC->GetData(kStyleMoleculeSID); + nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMol); + nscoord bottomMargin = kidMol->margin.bottom; + NS_RELEASE(kidSC); + + // 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) { + kidAvailSize.height -= topMargin; + } + // Subtract off for left and right margin + if (PR_FALSE == aState.unconstrainedWidth) { + kidAvailSize.width -= kidMol->margin.left + kidMol->margin.right; + } + + if (NS_UNCONSTRAINEDSIZE == aState.availSize.width) + { + // Reflow the child into the available space + status = ReflowChild(kidFrame, aPresContext, desiredSize, + kidAvailSize, pKidMaxElementSize); + nsCellLayoutData kidLayoutData((nsTableCellFrame *)kidFrame, &desiredSize, pKidMaxElementSize); + aState.tableFrame->SetCellLayoutData(&kidLayoutData, (nsTableCell *)cell); + } + else + { // we're in a constrained situation, so get the avail width from the known column widths + nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCell *)cell); + PRInt32 cellStartingCol = ((nsTableCell *)cell)->GetColIndex(); + PRInt32 cellColSpan = ((nsTableCell *)cell)->GetColSpan(); + nscoord availWidth = 0; + for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); + NS_ASSERTION(0width, pKidMaxElementSize->height); + else + printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n", + status==frComplete?"complete":"NOT complete", + desiredSize.width, desiredSize.height); + } + } + NS_RELEASE(cell); // cell: REFCNT-- + // Did the child fit? + if ((kidFrame != mFirstChild) && + ((kidAvailSize.height <= 0) || + (desiredSize.height > kidAvailSize.height))) + { + // The child's height is too big to fit at all in our remaining 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, lastContentIsComplete); + SetLastContentOffset(prevKidFrame); + + // Our mLastContentIsComplete was already set by the last kid we + // reflowed reflow's status + result = PR_FALSE; + break; + } + + // Adjust the running x-offset, taking into account intruders (cells from prior rows with rowspans > 1) + aState.x = 0; + PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex(); + for (PRInt32 colIndex=0; colIndexGetColumnWidth(colIndex); + // Place the child after taking into account it's margin + nsRect kidRect (aState.x, topMargin, desiredSize.width, desiredSize.height); + PlaceChild(aPresContext, 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 = PRBool(status == frComplete); + + /* Rows should not create continuing frames for cells + * unless they absolutely have to! + * check to see if this is absolutely necessary (with new params from troy) + * otherwise PushChildren and bail. + */ + // Special handling for incomplete children + if (frNotComplete == status) { + // XXX It's good to assume that we might still have room + // even if the child didn't complete (floaters will want this) + nsIFrame* kidNextInFlow; + + kidFrame->GetNextInFlow(kidNextInFlow); + PRBool lastContentIsComplete = mLastContentIsComplete; + if (nsnull == kidNextInFlow) { + // No the child isn't complete, and it doesn't have a next in flow so + // create a continuing frame. This hooks the child into the flow. + nsIFrame* continuingFrame; + + kidFrame->CreateContinuingFrame(aPresContext, this, continuingFrame); + + // Insert the frame. We'll reflow it next pass through the loop + 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); + SetLastContentOffset(prevKidFrame); + } + result = PR_FALSE; + break; + } + + // Add back in the left and right margins, because one row does not + // impact another row's width + if (PR_FALSE == aState.unconstrainedWidth) { + kidAvailSize.width += kidMol->margin.left + kidMol->margin.right; + } + + // Get the next child + kidFrame->GetNextSibling(kidFrame); + + } + + SetMaxChildHeight(aState.maxCellHeight); // remember height of tallest child who doesn't have a row span + + // Update the child count + mChildCount = childCount; + +#ifdef NS_DEBUG + NS_POSTCONDITION(LengthOf(mFirstChild) == mChildCount, "bad child count"); + + nsIFrame* lastChild; + PRInt32 lastIndexInParent; + + LastChild(lastChild); + lastChild->GetIndexInParent(lastIndexInParent); + NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); +#endif + +#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 + { + nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow; + while (flow != 0) { + printf(" %p: [%d,%d,%c]\n", + flow, flow->mFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) 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 nsTableRowFrame::PullUpChildren(nsIPresContext* aPresContext, + RowReflowState& 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 + { + nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow; + while (flow != 0) { + printf(" %p: [%d,%d,%c]\n", + flow, flow->mFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) flow->mNextInFlow; + } + } +#endif +#endif + nsTableRowFrame* nextInFlow = (nsTableRowFrame*)mNextInFlow; + nsSize kidMaxElementSize; + nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull; + nsSize kidAvailSize(aState.availSize); +#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 desiredSize; + ReflowStatus 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; + SplittableType kidIsSplittable; + + kidFrame->GetSize(kidFrameSize); + kidFrame->IsSplittable(kidIsSplittable); + + if ((kidFrameSize.height > aState.availSize.height) && + (kidIsSplittable == frNotSplittable)) { + result = PR_FALSE; + mLastContentIsComplete = prevLastContentIsComplete; + break; + } + + nsIContent *cell = nsnull; + kidFrame->GetContent(cell); // cell: REFNCT++ + // we're in a constrained situation, so get the avail width from the known column widths + nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCell *)cell); + PRInt32 cellStartingCol = ((nsTableCell *)cell)->GetColIndex(); + PRInt32 cellColSpan = ((nsTableCell *)cell)->GetColSpan(); + nscoord availWidth = 0; + for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); + NS_ASSERTION(0width, pKidMaxElementSize->height); + else + printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n", + status==frComplete?"complete":"NOT complete", + desiredSize.width, desiredSize.height); + } + } + NS_RELEASE(cell); // cell: REFCNT-- + + // Did the child fit? + if ((desiredSize.height > aState.availSize.height) && (nsnull != mFirstChild)) { + // The child is too tall to fit in the available space, and it's + // not our first child + result = PR_FALSE; + mLastContentIsComplete = prevLastContentIsComplete; + break; + } + + // Adjust the running x-offset, taking into account intruders (cells from prior rows with rowspans > 1) + aState.x = 0; + PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex(); + for (PRInt32 colIndex=0; colIndexGetColumnWidth(colIndex); + // Place the child + //nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMol); + nsRect kidRect (aState.x, 0, desiredSize.width, desiredSize.height); + PlaceChild(aPresContext, 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 = PRBool(status == frComplete); + if (frNotComplete == 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; + + kidFrame->CreateContinuingFrame(aPresContext, this, 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 = (nsTableRowFrame*)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 + 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 + { + nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow; + while (flow != 0) { + printf(" %p: [%d,%d,%c]\n", + flow, flow->mFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) flow->mNextInFlow; + } + } +#endif +#endif + return result; +} + +/** + * Create new frames for content we haven't yet mapped + * + * @param aPresContext presentation context to use + * @param aState current inline state + * @return frComplete if all content has been mapped and frNotComplete + * if we should be continued + */ +nsIFrame::ReflowStatus +nsTableRowFrame::ReflowUnmappedChildren( nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize) +{ +#ifdef NS_DEBUG + VerifyLastIsComplete(); +#endif + nsIFrame* kidPrevInFlow = nsnull; + ReflowStatus result = frNotComplete; + + // If we have no children and we have a prev-in-flow then we need to pick + // up where it left off. If we have children, e.g. we're being resized, then + // our content offset should already be set correctly... + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsTableRowFrame* prev = (nsTableRowFrame*)mPrevInFlow; + NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); + + mFirstContentOffset = prev->NextChildOffset(); + if (!prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + mLastContentIsComplete = PR_TRUE; + + // Place our children, one at a time, until we are out of children + nsSize kidMaxElementSize; + nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull; + nsSize kidAvailSize(aState.availSize); + PRInt32 kidIndex = NextChildOffset(); + nsIFrame* prevKidFrame; + + LastChild(prevKidFrame); // XXX remember this... + + for (;;) { + // Get the next content object + nsIContent* cell = mContent->ChildAt(kidIndex); + if (nsnull == cell) { + result = frComplete; + break; + } + + // Make sure we still have room left + if (aState.availSize.height <= 0) { + // Note: return status was set to frNotComplete above... + NS_RELEASE(cell); // cell: REFCNT--(a) + break; + } + + // Resolve style + nsIStyleContext* kidStyleContext = + aPresContext->ResolveStyleContextFor(cell, this); + nsStyleMolecule* kidMol = + (nsStyleMolecule*)kidStyleContext->GetData(kStyleMoleculeSID); + nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMol); + nscoord bottomMargin = kidMol->margin.bottom; + + nsIFrame* kidFrame; + + // Create a child frame + if (nsnull == kidPrevInFlow) { + nsIContentDelegate* kidDel = nsnull; + kidDel = cell->GetDelegate(aPresContext); + kidFrame = kidDel->CreateFrame(aPresContext, cell, kidIndex, this); + NS_RELEASE(kidDel); + kidFrame->SetStyleContext(kidStyleContext); + } else { + kidPrevInFlow->CreateContinuingFrame(aPresContext, this, kidFrame); + } + NS_RELEASE(kidStyleContext); + + // Try to reflow the child into the available space. It might not + // fit or might need continuing. + nsReflowMetrics desiredSize; + ReflowStatus status; + if (NS_UNCONSTRAINEDSIZE == aState.availSize.width) + { + // Reflow the child into the available space + status = ReflowChild(kidFrame, aPresContext, desiredSize, + kidAvailSize, pKidMaxElementSize); + nsCellLayoutData kidLayoutData((nsTableCellFrame *)kidFrame, &desiredSize, pKidMaxElementSize); + aState.tableFrame->SetCellLayoutData(&kidLayoutData, (nsTableCell *)cell); + } + else + { // we're in a constrained situation, so get the avail width from the known column widths + nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCell *)cell); + PRInt32 cellStartingCol = ((nsTableCell *)cell)->GetColIndex(); + PRInt32 cellColSpan = ((nsTableCell *)cell)->GetColSpan(); + nscoord availWidth = 0; + for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); + NS_ASSERTION(0width, pKidMaxElementSize->height); + else + printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n", + status==frComplete?"complete":"NOT complete", + desiredSize.width, desiredSize.height); + } + } + NS_RELEASE(cell); // cell: REFCNT-- + + // Did the child fit? + if ((desiredSize.height > aState.availSize.height) && (nsnull != mFirstChild)) { + // The child is too wide to fit in the available space, and it's + // not our first child. Add the frame to our overflow list + NS_ASSERTION(nsnull == mOverflowList, "bad overflow list"); + mOverflowList = kidFrame; + prevKidFrame->SetNextSibling(nsnull); + break; + } + + aState.x = 0; + // if we're constrained, compute the x offset where the cell should be put + if (NS_UNCONSTRAINEDSIZE!=aState.availSize.width) + { + // Adjust the running x-offset, taking into account intruders (cells from prior rows with rowspans > 1) + PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex(); + for (PRInt32 colIndex=0; colIndexGetColumnWidth(colIndex); + } + // Place the child + nsRect kidRect (aState.x, topMargin, desiredSize.width, desiredSize.height); + PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize, *pKidMaxElementSize); + + // Link child frame into the list of children + if (nsnull != prevKidFrame) { + prevKidFrame->SetNextSibling(kidFrame); + } else { + mFirstChild = kidFrame; // our first child + SetFirstContentOffset(kidFrame); + } + prevKidFrame = kidFrame; + mChildCount++; + kidIndex++; + + // Did the child complete? + if (frNotComplete == status) { + // If the child isn't complete then it means that we've used up + // all of our available space + mLastContentIsComplete = PR_FALSE; + break; + } + kidPrevInFlow = nsnull; + } + + SetMaxChildHeight(aState.maxCellHeight); // remember height of tallest child who doesn't have a row span + + // Update the content mapping + NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); + SetLastContentOffset(prevKidFrame); +#ifdef NS_DEBUG + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(len == mChildCount, "bad child count"); +#endif +#ifdef NS_DEBUG + VerifyLastIsComplete(); +#endif + return result; +} - */ /** Layout the entire row. * This method stacks rows horizontally according to HTML 4.0 rules. - * Rows are responsible for layout of their children. + * Rows are responsible for layout of their children (cells). */ NS_METHOD nsTableRowFrame::ResizeReflow(nsIPresContext* aPresContext, @@ -139,176 +933,75 @@ nsTableRowFrame::ResizeReflow(nsIPresContext* aPresContext, nsSize* aMaxElementSize, ReflowStatus& aStatus) { - if (gsDebug1==PR_TRUE) - printf("nsTableRowFrame::ResizeReflow - %p with aMaxSize = %d, %d\n", - this, aMaxSize.width, aMaxSize.height); + if (gsDebug1==PR_TRUE) + printf("nsTableRowFrame::ResizeReflow - aMaxSize = %d, %d\n", + aMaxSize.width, aMaxSize.height); #ifdef NS_DEBUG PreReflowCheck(); #endif - //nsresult result = NS_OK; - PRInt32 maxCellHeight = 0; + // Initialize out parameter + if (nsnull != aMaxElementSize) { + aMaxElementSize->width = 0; + aMaxElementSize->height = 0; + } + + // Initialize our internal data ResetMaxChildHeight(); + PRBool reflowMappedOK = PR_TRUE; + aStatus = frComplete; - mFirstContentOffset = mLastContentOffset = 0; + // Check for an overflow list + MoveOverflowToChildList(); - nsSize availSize(aMaxSize); - nsSize maxSize(0, 0); - nsSize kidMaxSize(0,0); - nsSize kidMaxElementSize(0,0); - nsReflowMetrics kidSize; - nscoord cellXOffset = 0; - nscoord maxAscent = 0; - nscoord maxDescent = 0; - PRInt32 kidIndex = 0; - PRInt32 lastIndex = mContent->ChildCount(); - nsIFrame* prevKidFrame = nsnull;/* XXX incremental reflow! */ + nsStyleMolecule* myMol = + (nsStyleMolecule*)mStyleContext->GetData(kStyleMoleculeSID); + RowReflowState state(aPresContext, aMaxSize, myMol); + mContentParent->GetContentParent((nsIFrame*&)(state.tableFrame)); -// Row doesn't factor in insets, the cells do that - - nsTableFrame *tableFrame; - - mContentParent->GetContentParent((nsIFrame*&)tableFrame); - - for (;;) { - nsTableCell* cell = (nsTableCell *)(mContent->ChildAt(kidIndex)); // cell: REFCNT++ - if (nsnull == cell) { - aStatus = frComplete; - break; + // Reflow the existing frames + if (nsnull != mFirstChild) { + reflowMappedOK = ReflowMappedChildren(aPresContext, state, aMaxElementSize); + if (PR_FALSE == reflowMappedOK) { + aStatus = frNotComplete; } - - // get next frame, creating one if needed - nsIFrame* kidFrame=nsnull; - if (nsnull!=prevKidFrame) - prevKidFrame->GetNextSibling(kidFrame); // no need to check for an error, just see if it returned null... - else - ChildAt(0, kidFrame); - if (nsnull==kidFrame) - { - nsIContentDelegate* kidDel; - kidDel = cell->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, cell, kidIndex, this); - mChildCount++; - NS_RELEASE(kidDel); - // Resolve style - nsIStyleContext* kidStyleContext = - aPresContext->ResolveStyleContextFor(cell, this); - NS_ASSERTION(nsnull!=kidStyleContext, "bad style context for kid."); - kidFrame->SetStyleContext(kidStyleContext); - NS_RELEASE(kidStyleContext); - } - - // Try to reflow the child into the available space. - if (NS_UNCONSTRAINEDSIZE == availSize.width) - { // Each cell is given the entire row width to try to lay out into - aStatus = ReflowChild(kidFrame, aPresContext, kidSize, availSize, &kidMaxSize); - if (gsDebug1) printf("reflow of cell returned result = %s with desired=%d,%d, min = %d,%d\n", - aStatus==frComplete?"complete":"NOT complete", - kidSize.width, kidSize.height, kidMaxSize.width, kidMaxSize.height); - nsCellLayoutData kidLayoutData((nsTableCellFrame *)kidFrame, &kidSize, &kidMaxSize); - tableFrame->SetCellLayoutData(&kidLayoutData, cell); - } - else - { // we're in a constrained situation, so get the avail width from the known column widths - nsTableFrame *innerTableFrame; - - mContentParent->GetContentParent((nsIFrame*&)innerTableFrame); - nsCellLayoutData *cellData = innerTableFrame->GetCellLayoutData(cell); - PRInt32 cellStartingCol = cell->GetColIndex(); - PRInt32 cellColSpan = cell->GetColSpan(); - PRInt32 availWidth = 0; - for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); - NS_ASSERTION(0GetColIndex(); colIndex++) - cellXOffset += innerTableFrame->GetColumnWidth(colIndex); - } - PRInt32 rowSpan = cell->GetRowSpan(); - if ((1==rowSpan) && (maxCellHeightSetRect(nsRect(cellXOffset, 0, kidSize.width, kidSize.height)); - if (kidSize.width > maxSize.width) { - maxSize.width = kidSize.width; - } - if (NS_UNCONSTRAINEDSIZE==aMaxSize.height) - { - if ((1==rowSpan) && (kidSize.height > maxSize.height)) - maxSize.height = kidSize.height; - if (kidMaxSize.height > kidMaxElementSize.height) - kidMaxElementSize.height = kidMaxSize.height; - } - else - { // in the constrained height case, our maxElementSize is the height of our tallest cell - if ((1==rowSpan) && (kidSize.height > maxSize.height)) - maxSize.height = kidSize.height; - if (maxSize.height > kidMaxElementSize.height) - kidMaxElementSize.height = maxSize.height; - } - kidMaxElementSize.width += kidMaxSize.width; - if (NS_UNCONSTRAINEDSIZE!=cellXOffset) - cellXOffset+=kidSize.width; - if (cellXOffset<=0) - cellXOffset = NS_UNCONSTRAINEDSIZE; // handle overflow - - // Link child frame into the list of children - if (nsnull != prevKidFrame) { - prevKidFrame->SetNextSibling(kidFrame); - } else { - // Our first child (**blush**) - if (gsDebug1) printf ("row frame %p, setting first child to %p\n", this, kidFrame); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - if (gsDebug1) printf("ROW: set first content offset to %d\n", GetFirstContentOffset()); //@@@ - } - prevKidFrame = kidFrame; - - kidIndex++; - if (frNotComplete == aStatus) { - // If the child didn't finish layout then it means that it used - // up all of our available space (or needs us to split). - mLastContentIsComplete = PR_FALSE; - break; - } - NS_RELEASE(cell); // kid: REFCNT-- } - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - if (!IsLastChild(prevKidFrame)) - { - mGeometricParent->List(); + // 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 = frNotComplete; + } + } else if (NextChildOffset() < mContent->ChildCount()) { + // Try and pull-up some children from a next-in-flow + if (PullUpChildren(aPresContext, state, aMaxElementSize)) { + // If we still have unmapped children then create some new frames + if (NextChildOffset() < mContent->ChildCount()) { + aStatus = ReflowUnmappedChildren(aPresContext, state, aMaxElementSize); + } + } else { + // We were unable to pull-up all the existing frames from the + // next in flow + aStatus = frNotComplete; + } } -#endif - NS_ASSERTION(IsLastChild(prevKidFrame), "unexpected last child"); - SetLastContentOffset(prevKidFrame); } - // Return our size and our result - aDesiredSize.width = cellXOffset; - aDesiredSize.height = maxSize.height; - aDesiredSize.ascent = maxAscent; - aDesiredSize.descent = maxDescent; - if (nsnull != aMaxElementSize) { - *aMaxElementSize = kidMaxElementSize; + if (frComplete == 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; } - SetMaxChildHeight(maxCellHeight); // remember height of tallest child who doesn't have a row span + + // Return our desired rect + aDesiredSize.width = aMaxSize.width; + aDesiredSize.height = state.maxCellHeight; #ifdef NS_DEBUG PostReflowCheck(aStatus); @@ -326,10 +1019,9 @@ nsTableRowFrame::ResizeReflow(nsIPresContext* aPresContext, aStatus==frComplete?"Complete":"Not Complete", aDesiredSize.width, aDesiredSize.height); } - - // testing... - aStatus = frComplete; + return NS_OK; + } NS_METHOD diff --git a/mozilla/layout/html/table/src/nsTableRowFrame.h b/mozilla/layout/html/table/src/nsTableRowFrame.h index 20446c32384..c10efc5ba8b 100644 --- a/mozilla/layout/html/table/src/nsTableRowFrame.h +++ b/mozilla/layout/html/table/src/nsTableRowFrame.h @@ -21,6 +21,8 @@ #include "nscore.h" #include "nsContainerFrame.h" +struct RowReflowState; + /** * nsTableRowFrame is the frame that maps table rows @@ -110,12 +112,60 @@ protected: /** protected constructor. * @see NewFrame */ - nsTableRowFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParentFrame); + nsTableRowFrame(nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParentFrame); + + /** destructor */ + virtual ~nsTableRowFrame(); + + nscoord GetTopMarginFor(nsIPresContext* aCX, + RowReflowState& aState, + nsStyleMolecule* aKidMol); + + void PlaceChild( nsIPresContext* aPresContext, + RowReflowState& aState, + nsIFrame* aKidFrame, + const nsRect& aKidRect, + nsSize* aMaxElementSize, + nsSize& aKidMaxElementSize); + + /** + * 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 ReflowMappedChildren(nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize); + + /** + * 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 PullUpChildren(nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize); + + /** + * Create new frames for content we haven't yet mapped + * + * @param aPresContext presentation context to use + * @param aState current inline state + * @return frComplete if all content has been mapped and frNotComplete + * if we should be continued + */ + ReflowStatus ReflowUnmappedChildren(nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize); - /** destructor */ - virtual ~nsTableRowFrame(); private: PRInt32 mTallestCell; // not my height, but the height of my tallest child diff --git a/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp b/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp index 06689af4349..e43975e4609 100644 --- a/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableRowGroupFrame.cpp @@ -95,14 +95,24 @@ struct RowGroupReflowState { /* ----------- nsTableRowGroupFrame ---------- */ nsTableRowGroupFrame::nsTableRowGroupFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParentFrame) + PRInt32 aIndexInParent, + nsIFrame* aParentFrame) : nsContainerFrame(aContent, aIndexInParent, aParentFrame) { + mType = aContent->GetTag(); // mType: REFCNT++ } nsTableRowGroupFrame::~nsTableRowGroupFrame() { + if (nsnull!=mType) + NS_RELEASE(mType); // mType: REFCNT-- +} + +NS_METHOD nsTableRowGroupFrame::GetRowGroupType(nsIAtom *& aType) +{ + NS_ADDREF(mType); + aType=mType; + return NS_OK; } @@ -264,6 +274,8 @@ PRBool nsTableRowGroupFrame::ReflowMappedChildren( nsIPresContext* aPresCon for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) { nsSize kidAvailSize(aState.availSize); + if (0>=kidAvailSize.height) + kidAvailSize.height = 1; // XXX: HaCk - we don't handle negative heights yet nsReflowMetrics desiredSize; nsIFrame::ReflowStatus status; @@ -290,7 +302,7 @@ PRBool nsTableRowGroupFrame::ReflowMappedChildren( nsIPresContext* aPresCon // Reflow the child into the available space status = ReflowChild(kidFrame, aPresContext, desiredSize, - kidAvailSize, pKidMaxElementSize); + kidAvailSize, pKidMaxElementSize); // Did the child fit? if ((kidFrame != mFirstChild) && @@ -381,6 +393,12 @@ PRBool nsTableRowGroupFrame::ReflowMappedChildren( nsIPresContext* aPresCon break; } + // Add back in the left and right margins, because one row does not + // impact another row's width + if (PR_FALSE == aState.unconstrainedWidth) { + kidAvailSize.width += kidMol->margin.left + kidMol->margin.right; + } + // Get the next child kidFrame->GetNextSibling(kidFrame); @@ -868,10 +886,10 @@ nsTableRowGroupFrame::ResizeReflow( nsIPresContext* aPresContext, } // Return our desired rect - NS_ASSERTION(0GetColSpan(); + } + return result; +} + +/** helper method to get the col index of this frame's content (which must be a cell) */ +PRInt32 nsTableCellFrame::GetColIndex() +{ + PRInt32 result = 0; + nsTableCell *cellContent = (nsTableCell *)mContent; + if (nsnull!=cellContent) + { + result = cellContent->GetColIndex(); + } + return result; +} + void nsTableCellFrame::CreatePsuedoFrame(nsIPresContext* aPresContext) { // Do we have a prev-in-flow? @@ -229,6 +253,8 @@ NS_METHOD nsTableCellFrame::ResizeReflow(nsIPresContext* aPresContext, // Try to reflow the child into the available space. It might not // fit or might need continuing. + if (availSize.height < 0) + availSize.height = 1; nsSize maxKidElementSize; if (gsDebug==PR_TRUE) printf(" nsTableCellFrame::ResizeReflow calling ReflowChild with availSize=%d,%d\n", diff --git a/mozilla/layout/tables/nsTableCellFrame.h b/mozilla/layout/tables/nsTableCellFrame.h index 3199b6efa5d..65daa2c299a 100644 --- a/mozilla/layout/tables/nsTableCellFrame.h +++ b/mozilla/layout/tables/nsTableCellFrame.h @@ -63,8 +63,15 @@ public: void VerticallyAlignChild(nsIPresContext* aPresContext); + /** return the mapped cell's row span. Always >= 1. */ virtual PRInt32 GetRowSpan(); + /** return the mapped cell's col span. Always >= 1. */ + virtual PRInt32 GetColSpan(); + + /** return the mapped cell's column index (starting at 0 for the first column) */ + virtual PRInt32 GetColIndex(); + virtual ~nsTableCellFrame(); protected: diff --git a/mozilla/layout/tables/nsTableFrame.cpp b/mozilla/layout/tables/nsTableFrame.cpp index e4de2748789..f34bd8818ce 100644 --- a/mozilla/layout/tables/nsTableFrame.cpp +++ b/mozilla/layout/tables/nsTableFrame.cpp @@ -90,6 +90,12 @@ struct InnerTableReflowState { // Flag for whether we're dealing with the first interior row group PRBool firstRowGroup; + // a list of the footers in this table frame, for quick access when inserting bodies + nsVoidArray *footerList; + + // cache the total height of the footers for placing body rows + nscoord footerHeight; + InnerTableReflowState(nsIPresContext* aPresContext, const nsSize& aMaxSize, nsStyleMolecule* aMol) @@ -103,9 +109,13 @@ struct InnerTableReflowState { unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); firstRowGroup = PR_TRUE; + footerHeight = 0; + footerList = nsnull; } ~InnerTableReflowState() { + if (nsnull!=footerList) + delete footerList; } }; @@ -712,7 +722,7 @@ nsIFrame::ReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresCont nsIFrame* prevKidFrame = nsnull;/* XXX incremental reflow! */ #ifdef NS_DEBUG - PreReflowCheck(); + //PreReflowCheck(); #endif // Initialize out parameter @@ -775,7 +785,7 @@ nsIFrame::ReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresCont } // Return our desired rect - NS_ASSERTION(0GetRowGroupType(kidType); + if (tFootTag==kidType) + { + if (nsnull==aState.footerList) + aState.footerList = new nsVoidArray(); + aState.footerList->AppendElement((void *)aKidFrame); + aState.footerHeight += aKidRect.height; + } + // else if this is a body row group, push down all the footer row groups + else + { + // don't bother unless there are footers to push down + if (nsnull!=aState.footerList && 0!=aState.footerList->Count()) + { + nsPoint origin; + aKidFrame->GetOrigin(origin); + origin.y -= aState.footerHeight; + aKidFrame->MoveTo(origin.x, origin.y); + nsIAtom * tBodyTag = NS_NewAtom(nsTablePart::kRowGroupBodyTagString); // tBodyTag: REFCNT++ + if (tBodyTag==kidType) + { + PRInt32 numFooters = aState.footerList->Count(); + for (PRInt32 footerIndex = 0; footerIndex < numFooters; footerIndex++) + { + nsTableRowGroupFrame * footer = (nsTableRowGroupFrame *)(aState.footerList->ElementAt(footerIndex)); + NS_ASSERTION(nsnull!=footer, "bad footer list in table inner frame."); + if (nsnull!=footer) + { + footer->GetOrigin(origin); + origin.y += aKidRect.height; + footer->MoveTo(origin.x, origin.y); + } + } + } + NS_RELEASE(tBodyTag); + } + } + NS_RELEASE(tFootTag); + // Update the maximum element size if (PR_TRUE==aState.firstRowGroup) { @@ -1284,8 +1336,8 @@ PRBool nsTableFrame::PullUpChildren(nsIPresContext* aPresContext, */ nsIFrame::ReflowStatus nsTableFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext, - InnerTableReflowState& aState, - nsSize* aMaxElementSize) + InnerTableReflowState& aState, + nsSize* aMaxElementSize) { #ifdef NS_DEBUG VerifyLastIsComplete(); @@ -2441,16 +2493,53 @@ NS_METHOD nsTableFrame::CreateContinuingFrame(nsIPresContext* aPresContext, cf->SetRect(nsRect(0, 0, mRect.width, 0)); // add headers and footers to cf nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow(); - PRInt32 childCount; - - firstInFlow->ChildCount(childCount); - PRInt32 childIndex = 0; - for (; childIndex < childCount; childIndex++) + nsIFrame * rg = nsnull; + firstInFlow->ChildAt(0, rg); + NS_ASSERTION (nsnull!=rg, "previous frame has no children"); + nsIAtom * tHeadTag = NS_NewAtom(nsTablePart::kRowGroupHeadTagString); // tHeadTag: REFCNT++ + nsIAtom * tFootTag = NS_NewAtom(nsTablePart::kRowGroupFootTagString); // tFootTag: REFCNT++ + PRInt32 index = 0; + nsIFrame * bodyRowGroupFromOverflow = mOverflowList; + nsIFrame * lastSib = nsnull; + for ( ; nsnull!=rg; index++) { - // TODO: place copies of the header and footer row groups here - // maybe need to do this in ResizeReflow at the beginning, when we determine we are a continuing frame + nsIContent *content = nsnull; + rg->GetContent(content); // content: REFCNT++ + NS_ASSERTION(nsnull!=content, "bad frame, returned null content."); + nsIAtom * rgTag = content->GetTag(); + // if we've found a header or a footer, replicate it + if (tHeadTag==rgTag || tFootTag==rgTag) + { + printf("found a head or foot in continuing frame\n"); + // Resolve style for the child + nsIStyleContext* kidStyleContext = + aPresContext->ResolveStyleContextFor(content, cf); // kidStyleContext: REFCNT++ + nsStyleMolecule* kidMol = + (nsStyleMolecule*)kidStyleContext->GetData(kStyleMoleculeSID); + nsIContentDelegate* kidDel = nsnull; + kidDel = content->GetDelegate(aPresContext); // kidDel: REFCNT++ + nsIFrame * duplicateFrame = kidDel->CreateFrame(aPresContext, content, index, cf); + NS_RELEASE(kidDel); // kidDel: REFCNT-- + duplicateFrame->SetStyleContext(kidStyleContext); + NS_RELEASE(kidStyleContext); // kidStyleContenxt: REFCNT-- + + if (nsnull==lastSib) + { + mOverflowList = duplicateFrame; + } + else + { + lastSib->SetNextSibling(duplicateFrame); + } + duplicateFrame->SetNextSibling(bodyRowGroupFromOverflow); + lastSib = duplicateFrame; + } + NS_RELEASE(content); // content: REFCNT-- + // get the next row group + rg->GetNextSibling(rg); } - + NS_RELEASE(tFootTag); // tHeadTag: REFCNT -- + NS_RELEASE(tHeadTag); // tFootTag: REFCNT -- aContinuingFrame = cf; return NS_OK; } diff --git a/mozilla/layout/tables/nsTableOuterFrame.cpp b/mozilla/layout/tables/nsTableOuterFrame.cpp index 2439865157f..a33de28ae1f 100644 --- a/mozilla/layout/tables/nsTableOuterFrame.cpp +++ b/mozilla/layout/tables/nsTableOuterFrame.cpp @@ -305,7 +305,13 @@ NS_METHOD nsTableOuterFrame::ResizeReflow(nsIPresContext* aPresContext, //NS_ASSERTION(0 aState.availSize.height) && (kidFrame != mFirstChild)) { - // The child is too wide to fit in the available space, and it's + // 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 @@ -518,8 +524,6 @@ PRBool nsTableOuterFrame::ReflowMappedChildren( nsIPresContext* aPresContex // Update mLastContentIsComplete now that this kid fits mLastContentIsComplete = PRBool(status == frComplete); - // Is the child complete? - mLastContentIsComplete = PRBool(status == frComplete); if (frNotComplete == status) { // No, the child isn't complete nsIFrame* kidNextInFlow; @@ -555,6 +559,21 @@ PRBool nsTableOuterFrame::ReflowMappedChildren( nsIPresContext* aPresContex 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; diff --git a/mozilla/layout/tables/nsTableRowFrame.cpp b/mozilla/layout/tables/nsTableRowFrame.cpp index 2af64ea1fb8..318e92d5cbb 100644 --- a/mozilla/layout/tables/nsTableRowFrame.cpp +++ b/mozilla/layout/tables/nsTableRowFrame.cpp @@ -40,9 +40,61 @@ static const PRBool gsDebug2 = PR_FALSE; static NS_DEFINE_IID(kStyleMoleculeSID, NS_STYLEMOLECULE_SID); +/* ----------- RowReflowState ---------- */ + +struct RowReflowState { + + // The body's style molecule + nsStyleMolecule* mol; + + // The body's available size (computed from the body's parent) + nsSize availSize; + + // Margin tracking information + nscoord prevMaxPosBottomMargin; + nscoord prevMaxNegBottomMargin; + + // Flags for whether the max size is unconstrained + PRBool unconstrainedWidth; + PRBool unconstrainedHeight; + + // Running x-offset + nscoord x; + + // Height of tallest cell (excluding cells with rowspan>1) + nscoord maxCellHeight; + + nsTableFrame *tableFrame; + + + RowReflowState( nsIPresContext* aPresContext, + const nsSize& aMaxSize, + nsStyleMolecule* aMol) + { + mol = aMol; + availSize.width = aMaxSize.width; + availSize.height = aMaxSize.height; + prevMaxPosBottomMargin = 0; + prevMaxNegBottomMargin = 0; + x=0; + unconstrainedWidth = PRBool(aMaxSize.width == NS_UNCONSTRAINEDSIZE); + unconstrainedHeight = PRBool(aMaxSize.height == NS_UNCONSTRAINEDSIZE); + maxCellHeight=0; + tableFrame = nsnull; + } + + ~RowReflowState() { + } +}; + + + + +/* ----------- nsTableRowpFrame ---------- */ + nsTableRowFrame::nsTableRowFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParentFrame) + PRInt32 aIndexInParent, + nsIFrame* aParentFrame) : nsContainerFrame(aContent, aIndexInParent, aParentFrame), mTallestCell(0) { @@ -120,17 +172,759 @@ PRInt32 nsTableRowFrame::GetTallestChild() const return mTallestCell; } -/* +// Collapse child's top margin with previous bottom margin +nscoord nsTableRowFrame::GetTopMarginFor( nsIPresContext* aCX, + RowReflowState& aState, + nsStyleMolecule* aKidMol) +{ + nscoord margin; + nscoord maxNegTopMargin = 0; + nscoord maxPosTopMargin = 0; + if ((margin = aKidMol->margin.top) < 0) { + maxNegTopMargin = -margin; + } else { + maxPosTopMargin = margin; + } -want to use min space the child can fit in to -determine its real size, unless fixed columns sizes -are present. + 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 nsTableRowFrame::PlaceChild(nsIPresContext* aPresContext, + RowReflowState& aState, + nsIFrame* aKidFrame, + const nsRect& aKidRect, + nsSize* aMaxElementSize, + nsSize& aKidMaxElementSize) +{ + if (PR_TRUE==gsDebug1) + printf ("row: placing cell at %d, %d, %d, %d\n", + aKidRect.x, aKidRect.y, aKidRect.width, aKidRect.height); + + // Place and size the child + aKidFrame->SetRect(aKidRect); + + // Update the maximum element size + PRInt32 rowSpan = ((nsTableCellFrame*)aKidFrame)->GetRowSpan(); + if (nsnull != aMaxElementSize) { + aMaxElementSize->width += aKidMaxElementSize.width; + if ((1==rowSpan) && (aKidMaxElementSize.height>aMaxElementSize->height)) + { + aMaxElementSize->height = aKidMaxElementSize.height; + } + } + if ((1==rowSpan) && (aState.maxCellHeightmFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) 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 lastContentIsComplete = mLastContentIsComplete; + + nsSize kidMaxElementSize; + nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull; + PRBool result = PR_TRUE; + + for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) { + nsIContent* cell = nsnull; + kidFrame->GetContent(cell); // cell: REFCNT++ + nsSize kidAvailSize(aState.availSize); + if (0>=kidAvailSize.height) + kidAvailSize.height = 1; // XXX: HaCk - we don't handle negative heights yet + nsReflowMetrics desiredSize; + nsIFrame::ReflowStatus status; + + // Get top margin for this kid + nsIStyleContext* kidSC; + kidFrame->GetStyleContext(aPresContext, kidSC); + nsStyleMolecule* kidMol = (nsStyleMolecule*)kidSC->GetData(kStyleMoleculeSID); + nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMol); + nscoord bottomMargin = kidMol->margin.bottom; + NS_RELEASE(kidSC); + + // 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) { + kidAvailSize.height -= topMargin; + } + // Subtract off for left and right margin + if (PR_FALSE == aState.unconstrainedWidth) { + kidAvailSize.width -= kidMol->margin.left + kidMol->margin.right; + } + + if (NS_UNCONSTRAINEDSIZE == aState.availSize.width) + { + // Reflow the child into the available space + status = ReflowChild(kidFrame, aPresContext, desiredSize, + kidAvailSize, pKidMaxElementSize); + nsCellLayoutData kidLayoutData((nsTableCellFrame *)kidFrame, &desiredSize, pKidMaxElementSize); + aState.tableFrame->SetCellLayoutData(&kidLayoutData, (nsTableCell *)cell); + } + else + { // we're in a constrained situation, so get the avail width from the known column widths + nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCell *)cell); + PRInt32 cellStartingCol = ((nsTableCell *)cell)->GetColIndex(); + PRInt32 cellColSpan = ((nsTableCell *)cell)->GetColSpan(); + nscoord availWidth = 0; + for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); + NS_ASSERTION(0width, pKidMaxElementSize->height); + else + printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n", + status==frComplete?"complete":"NOT complete", + desiredSize.width, desiredSize.height); + } + } + NS_RELEASE(cell); // cell: REFCNT-- + // Did the child fit? + if ((kidFrame != mFirstChild) && + ((kidAvailSize.height <= 0) || + (desiredSize.height > kidAvailSize.height))) + { + // The child's height is too big to fit at all in our remaining 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, lastContentIsComplete); + SetLastContentOffset(prevKidFrame); + + // Our mLastContentIsComplete was already set by the last kid we + // reflowed reflow's status + result = PR_FALSE; + break; + } + + // Adjust the running x-offset, taking into account intruders (cells from prior rows with rowspans > 1) + aState.x = 0; + PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex(); + for (PRInt32 colIndex=0; colIndexGetColumnWidth(colIndex); + // Place the child after taking into account it's margin + nsRect kidRect (aState.x, topMargin, desiredSize.width, desiredSize.height); + PlaceChild(aPresContext, 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 = PRBool(status == frComplete); + + /* Rows should not create continuing frames for cells + * unless they absolutely have to! + * check to see if this is absolutely necessary (with new params from troy) + * otherwise PushChildren and bail. + */ + // Special handling for incomplete children + if (frNotComplete == status) { + // XXX It's good to assume that we might still have room + // even if the child didn't complete (floaters will want this) + nsIFrame* kidNextInFlow; + + kidFrame->GetNextInFlow(kidNextInFlow); + PRBool lastContentIsComplete = mLastContentIsComplete; + if (nsnull == kidNextInFlow) { + // No the child isn't complete, and it doesn't have a next in flow so + // create a continuing frame. This hooks the child into the flow. + nsIFrame* continuingFrame; + + kidFrame->CreateContinuingFrame(aPresContext, this, continuingFrame); + + // Insert the frame. We'll reflow it next pass through the loop + 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); + SetLastContentOffset(prevKidFrame); + } + result = PR_FALSE; + break; + } + + // Add back in the left and right margins, because one row does not + // impact another row's width + if (PR_FALSE == aState.unconstrainedWidth) { + kidAvailSize.width += kidMol->margin.left + kidMol->margin.right; + } + + // Get the next child + kidFrame->GetNextSibling(kidFrame); + + } + + SetMaxChildHeight(aState.maxCellHeight); // remember height of tallest child who doesn't have a row span + + // Update the child count + mChildCount = childCount; + +#ifdef NS_DEBUG + NS_POSTCONDITION(LengthOf(mFirstChild) == mChildCount, "bad child count"); + + nsIFrame* lastChild; + PRInt32 lastIndexInParent; + + LastChild(lastChild); + lastChild->GetIndexInParent(lastIndexInParent); + NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset"); +#endif + +#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 + { + nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow; + while (flow != 0) { + printf(" %p: [%d,%d,%c]\n", + flow, flow->mFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) 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 nsTableRowFrame::PullUpChildren(nsIPresContext* aPresContext, + RowReflowState& 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 + { + nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow; + while (flow != 0) { + printf(" %p: [%d,%d,%c]\n", + flow, flow->mFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) flow->mNextInFlow; + } + } +#endif +#endif + nsTableRowFrame* nextInFlow = (nsTableRowFrame*)mNextInFlow; + nsSize kidMaxElementSize; + nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull; + nsSize kidAvailSize(aState.availSize); +#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 desiredSize; + ReflowStatus 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; + SplittableType kidIsSplittable; + + kidFrame->GetSize(kidFrameSize); + kidFrame->IsSplittable(kidIsSplittable); + + if ((kidFrameSize.height > aState.availSize.height) && + (kidIsSplittable == frNotSplittable)) { + result = PR_FALSE; + mLastContentIsComplete = prevLastContentIsComplete; + break; + } + + nsIContent *cell = nsnull; + kidFrame->GetContent(cell); // cell: REFNCT++ + // we're in a constrained situation, so get the avail width from the known column widths + nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCell *)cell); + PRInt32 cellStartingCol = ((nsTableCell *)cell)->GetColIndex(); + PRInt32 cellColSpan = ((nsTableCell *)cell)->GetColSpan(); + nscoord availWidth = 0; + for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); + NS_ASSERTION(0width, pKidMaxElementSize->height); + else + printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n", + status==frComplete?"complete":"NOT complete", + desiredSize.width, desiredSize.height); + } + } + NS_RELEASE(cell); // cell: REFCNT-- + + // Did the child fit? + if ((desiredSize.height > aState.availSize.height) && (nsnull != mFirstChild)) { + // The child is too tall to fit in the available space, and it's + // not our first child + result = PR_FALSE; + mLastContentIsComplete = prevLastContentIsComplete; + break; + } + + // Adjust the running x-offset, taking into account intruders (cells from prior rows with rowspans > 1) + aState.x = 0; + PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex(); + for (PRInt32 colIndex=0; colIndexGetColumnWidth(colIndex); + // Place the child + //nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMol); + nsRect kidRect (aState.x, 0, desiredSize.width, desiredSize.height); + PlaceChild(aPresContext, 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 = PRBool(status == frComplete); + if (frNotComplete == 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; + + kidFrame->CreateContinuingFrame(aPresContext, this, 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 = (nsTableRowFrame*)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 + 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 + { + nsTableRowFrame* flow = (nsTableRowFrame*) mNextInFlow; + while (flow != 0) { + printf(" %p: [%d,%d,%c]\n", + flow, flow->mFirstContentOffset, flow->mLastContentOffset, + (flow->mLastContentIsComplete ? 'T' : 'F')); + flow = (nsTableRowFrame*) flow->mNextInFlow; + } + } +#endif +#endif + return result; +} + +/** + * Create new frames for content we haven't yet mapped + * + * @param aPresContext presentation context to use + * @param aState current inline state + * @return frComplete if all content has been mapped and frNotComplete + * if we should be continued + */ +nsIFrame::ReflowStatus +nsTableRowFrame::ReflowUnmappedChildren( nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize) +{ +#ifdef NS_DEBUG + VerifyLastIsComplete(); +#endif + nsIFrame* kidPrevInFlow = nsnull; + ReflowStatus result = frNotComplete; + + // If we have no children and we have a prev-in-flow then we need to pick + // up where it left off. If we have children, e.g. we're being resized, then + // our content offset should already be set correctly... + if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) { + nsTableRowFrame* prev = (nsTableRowFrame*)mPrevInFlow; + NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow"); + + mFirstContentOffset = prev->NextChildOffset(); + if (!prev->mLastContentIsComplete) { + // Our prev-in-flow's last child is not complete + prev->LastChild(kidPrevInFlow); + } + } + + mLastContentIsComplete = PR_TRUE; + + // Place our children, one at a time, until we are out of children + nsSize kidMaxElementSize; + nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull; + nsSize kidAvailSize(aState.availSize); + PRInt32 kidIndex = NextChildOffset(); + nsIFrame* prevKidFrame; + + LastChild(prevKidFrame); // XXX remember this... + + for (;;) { + // Get the next content object + nsIContent* cell = mContent->ChildAt(kidIndex); + if (nsnull == cell) { + result = frComplete; + break; + } + + // Make sure we still have room left + if (aState.availSize.height <= 0) { + // Note: return status was set to frNotComplete above... + NS_RELEASE(cell); // cell: REFCNT--(a) + break; + } + + // Resolve style + nsIStyleContext* kidStyleContext = + aPresContext->ResolveStyleContextFor(cell, this); + nsStyleMolecule* kidMol = + (nsStyleMolecule*)kidStyleContext->GetData(kStyleMoleculeSID); + nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMol); + nscoord bottomMargin = kidMol->margin.bottom; + + nsIFrame* kidFrame; + + // Create a child frame + if (nsnull == kidPrevInFlow) { + nsIContentDelegate* kidDel = nsnull; + kidDel = cell->GetDelegate(aPresContext); + kidFrame = kidDel->CreateFrame(aPresContext, cell, kidIndex, this); + NS_RELEASE(kidDel); + kidFrame->SetStyleContext(kidStyleContext); + } else { + kidPrevInFlow->CreateContinuingFrame(aPresContext, this, kidFrame); + } + NS_RELEASE(kidStyleContext); + + // Try to reflow the child into the available space. It might not + // fit or might need continuing. + nsReflowMetrics desiredSize; + ReflowStatus status; + if (NS_UNCONSTRAINEDSIZE == aState.availSize.width) + { + // Reflow the child into the available space + status = ReflowChild(kidFrame, aPresContext, desiredSize, + kidAvailSize, pKidMaxElementSize); + nsCellLayoutData kidLayoutData((nsTableCellFrame *)kidFrame, &desiredSize, pKidMaxElementSize); + aState.tableFrame->SetCellLayoutData(&kidLayoutData, (nsTableCell *)cell); + } + else + { // we're in a constrained situation, so get the avail width from the known column widths + nsCellLayoutData *cellData = aState.tableFrame->GetCellLayoutData((nsTableCell *)cell); + PRInt32 cellStartingCol = ((nsTableCell *)cell)->GetColIndex(); + PRInt32 cellColSpan = ((nsTableCell *)cell)->GetColSpan(); + nscoord availWidth = 0; + for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); + NS_ASSERTION(0width, pKidMaxElementSize->height); + else + printf("reflow of cell returned result = %s with desired=%d,%d, min = nsnull\n", + status==frComplete?"complete":"NOT complete", + desiredSize.width, desiredSize.height); + } + } + NS_RELEASE(cell); // cell: REFCNT-- + + // Did the child fit? + if ((desiredSize.height > aState.availSize.height) && (nsnull != mFirstChild)) { + // The child is too wide to fit in the available space, and it's + // not our first child. Add the frame to our overflow list + NS_ASSERTION(nsnull == mOverflowList, "bad overflow list"); + mOverflowList = kidFrame; + prevKidFrame->SetNextSibling(nsnull); + break; + } + + aState.x = 0; + // if we're constrained, compute the x offset where the cell should be put + if (NS_UNCONSTRAINEDSIZE!=aState.availSize.width) + { + // Adjust the running x-offset, taking into account intruders (cells from prior rows with rowspans > 1) + PRInt32 cellColIndex = ((nsTableCellFrame *)kidFrame)->GetColIndex(); + for (PRInt32 colIndex=0; colIndexGetColumnWidth(colIndex); + } + // Place the child + nsRect kidRect (aState.x, topMargin, desiredSize.width, desiredSize.height); + PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize, *pKidMaxElementSize); + + // Link child frame into the list of children + if (nsnull != prevKidFrame) { + prevKidFrame->SetNextSibling(kidFrame); + } else { + mFirstChild = kidFrame; // our first child + SetFirstContentOffset(kidFrame); + } + prevKidFrame = kidFrame; + mChildCount++; + kidIndex++; + + // Did the child complete? + if (frNotComplete == status) { + // If the child isn't complete then it means that we've used up + // all of our available space + mLastContentIsComplete = PR_FALSE; + break; + } + kidPrevInFlow = nsnull; + } + + SetMaxChildHeight(aState.maxCellHeight); // remember height of tallest child who doesn't have a row span + + // Update the content mapping + NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child"); + SetLastContentOffset(prevKidFrame); +#ifdef NS_DEBUG + PRInt32 len = LengthOf(mFirstChild); + NS_ASSERTION(len == mChildCount, "bad child count"); +#endif +#ifdef NS_DEBUG + VerifyLastIsComplete(); +#endif + return result; +} - */ /** Layout the entire row. * This method stacks rows horizontally according to HTML 4.0 rules. - * Rows are responsible for layout of their children. + * Rows are responsible for layout of their children (cells). */ NS_METHOD nsTableRowFrame::ResizeReflow(nsIPresContext* aPresContext, @@ -139,176 +933,75 @@ nsTableRowFrame::ResizeReflow(nsIPresContext* aPresContext, nsSize* aMaxElementSize, ReflowStatus& aStatus) { - if (gsDebug1==PR_TRUE) - printf("nsTableRowFrame::ResizeReflow - %p with aMaxSize = %d, %d\n", - this, aMaxSize.width, aMaxSize.height); + if (gsDebug1==PR_TRUE) + printf("nsTableRowFrame::ResizeReflow - aMaxSize = %d, %d\n", + aMaxSize.width, aMaxSize.height); #ifdef NS_DEBUG PreReflowCheck(); #endif - //nsresult result = NS_OK; - PRInt32 maxCellHeight = 0; + // Initialize out parameter + if (nsnull != aMaxElementSize) { + aMaxElementSize->width = 0; + aMaxElementSize->height = 0; + } + + // Initialize our internal data ResetMaxChildHeight(); + PRBool reflowMappedOK = PR_TRUE; + aStatus = frComplete; - mFirstContentOffset = mLastContentOffset = 0; + // Check for an overflow list + MoveOverflowToChildList(); - nsSize availSize(aMaxSize); - nsSize maxSize(0, 0); - nsSize kidMaxSize(0,0); - nsSize kidMaxElementSize(0,0); - nsReflowMetrics kidSize; - nscoord cellXOffset = 0; - nscoord maxAscent = 0; - nscoord maxDescent = 0; - PRInt32 kidIndex = 0; - PRInt32 lastIndex = mContent->ChildCount(); - nsIFrame* prevKidFrame = nsnull;/* XXX incremental reflow! */ + nsStyleMolecule* myMol = + (nsStyleMolecule*)mStyleContext->GetData(kStyleMoleculeSID); + RowReflowState state(aPresContext, aMaxSize, myMol); + mContentParent->GetContentParent((nsIFrame*&)(state.tableFrame)); -// Row doesn't factor in insets, the cells do that - - nsTableFrame *tableFrame; - - mContentParent->GetContentParent((nsIFrame*&)tableFrame); - - for (;;) { - nsTableCell* cell = (nsTableCell *)(mContent->ChildAt(kidIndex)); // cell: REFCNT++ - if (nsnull == cell) { - aStatus = frComplete; - break; + // Reflow the existing frames + if (nsnull != mFirstChild) { + reflowMappedOK = ReflowMappedChildren(aPresContext, state, aMaxElementSize); + if (PR_FALSE == reflowMappedOK) { + aStatus = frNotComplete; } - - // get next frame, creating one if needed - nsIFrame* kidFrame=nsnull; - if (nsnull!=prevKidFrame) - prevKidFrame->GetNextSibling(kidFrame); // no need to check for an error, just see if it returned null... - else - ChildAt(0, kidFrame); - if (nsnull==kidFrame) - { - nsIContentDelegate* kidDel; - kidDel = cell->GetDelegate(aPresContext); - kidFrame = kidDel->CreateFrame(aPresContext, cell, kidIndex, this); - mChildCount++; - NS_RELEASE(kidDel); - // Resolve style - nsIStyleContext* kidStyleContext = - aPresContext->ResolveStyleContextFor(cell, this); - NS_ASSERTION(nsnull!=kidStyleContext, "bad style context for kid."); - kidFrame->SetStyleContext(kidStyleContext); - NS_RELEASE(kidStyleContext); - } - - // Try to reflow the child into the available space. - if (NS_UNCONSTRAINEDSIZE == availSize.width) - { // Each cell is given the entire row width to try to lay out into - aStatus = ReflowChild(kidFrame, aPresContext, kidSize, availSize, &kidMaxSize); - if (gsDebug1) printf("reflow of cell returned result = %s with desired=%d,%d, min = %d,%d\n", - aStatus==frComplete?"complete":"NOT complete", - kidSize.width, kidSize.height, kidMaxSize.width, kidMaxSize.height); - nsCellLayoutData kidLayoutData((nsTableCellFrame *)kidFrame, &kidSize, &kidMaxSize); - tableFrame->SetCellLayoutData(&kidLayoutData, cell); - } - else - { // we're in a constrained situation, so get the avail width from the known column widths - nsTableFrame *innerTableFrame; - - mContentParent->GetContentParent((nsIFrame*&)innerTableFrame); - nsCellLayoutData *cellData = innerTableFrame->GetCellLayoutData(cell); - PRInt32 cellStartingCol = cell->GetColIndex(); - PRInt32 cellColSpan = cell->GetColSpan(); - PRInt32 availWidth = 0; - for (PRInt32 numColSpan=0; numColSpanGetColumnWidth(cellStartingCol+numColSpan); - NS_ASSERTION(0GetColIndex(); colIndex++) - cellXOffset += innerTableFrame->GetColumnWidth(colIndex); - } - PRInt32 rowSpan = cell->GetRowSpan(); - if ((1==rowSpan) && (maxCellHeightSetRect(nsRect(cellXOffset, 0, kidSize.width, kidSize.height)); - if (kidSize.width > maxSize.width) { - maxSize.width = kidSize.width; - } - if (NS_UNCONSTRAINEDSIZE==aMaxSize.height) - { - if ((1==rowSpan) && (kidSize.height > maxSize.height)) - maxSize.height = kidSize.height; - if (kidMaxSize.height > kidMaxElementSize.height) - kidMaxElementSize.height = kidMaxSize.height; - } - else - { // in the constrained height case, our maxElementSize is the height of our tallest cell - if ((1==rowSpan) && (kidSize.height > maxSize.height)) - maxSize.height = kidSize.height; - if (maxSize.height > kidMaxElementSize.height) - kidMaxElementSize.height = maxSize.height; - } - kidMaxElementSize.width += kidMaxSize.width; - if (NS_UNCONSTRAINEDSIZE!=cellXOffset) - cellXOffset+=kidSize.width; - if (cellXOffset<=0) - cellXOffset = NS_UNCONSTRAINEDSIZE; // handle overflow - - // Link child frame into the list of children - if (nsnull != prevKidFrame) { - prevKidFrame->SetNextSibling(kidFrame); - } else { - // Our first child (**blush**) - if (gsDebug1) printf ("row frame %p, setting first child to %p\n", this, kidFrame); - mFirstChild = kidFrame; - SetFirstContentOffset(kidFrame); - if (gsDebug1) printf("ROW: set first content offset to %d\n", GetFirstContentOffset()); //@@@ - } - prevKidFrame = kidFrame; - - kidIndex++; - if (frNotComplete == aStatus) { - // If the child didn't finish layout then it means that it used - // up all of our available space (or needs us to split). - mLastContentIsComplete = PR_FALSE; - break; - } - NS_RELEASE(cell); // kid: REFCNT-- } - if (nsnull != prevKidFrame) { -#ifdef NS_DEBUG - if (!IsLastChild(prevKidFrame)) - { - mGeometricParent->List(); + // 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 = frNotComplete; + } + } else if (NextChildOffset() < mContent->ChildCount()) { + // Try and pull-up some children from a next-in-flow + if (PullUpChildren(aPresContext, state, aMaxElementSize)) { + // If we still have unmapped children then create some new frames + if (NextChildOffset() < mContent->ChildCount()) { + aStatus = ReflowUnmappedChildren(aPresContext, state, aMaxElementSize); + } + } else { + // We were unable to pull-up all the existing frames from the + // next in flow + aStatus = frNotComplete; + } } -#endif - NS_ASSERTION(IsLastChild(prevKidFrame), "unexpected last child"); - SetLastContentOffset(prevKidFrame); } - // Return our size and our result - aDesiredSize.width = cellXOffset; - aDesiredSize.height = maxSize.height; - aDesiredSize.ascent = maxAscent; - aDesiredSize.descent = maxDescent; - if (nsnull != aMaxElementSize) { - *aMaxElementSize = kidMaxElementSize; + if (frComplete == 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; } - SetMaxChildHeight(maxCellHeight); // remember height of tallest child who doesn't have a row span + + // Return our desired rect + aDesiredSize.width = aMaxSize.width; + aDesiredSize.height = state.maxCellHeight; #ifdef NS_DEBUG PostReflowCheck(aStatus); @@ -326,10 +1019,9 @@ nsTableRowFrame::ResizeReflow(nsIPresContext* aPresContext, aStatus==frComplete?"Complete":"Not Complete", aDesiredSize.width, aDesiredSize.height); } - - // testing... - aStatus = frComplete; + return NS_OK; + } NS_METHOD diff --git a/mozilla/layout/tables/nsTableRowFrame.h b/mozilla/layout/tables/nsTableRowFrame.h index 20446c32384..c10efc5ba8b 100644 --- a/mozilla/layout/tables/nsTableRowFrame.h +++ b/mozilla/layout/tables/nsTableRowFrame.h @@ -21,6 +21,8 @@ #include "nscore.h" #include "nsContainerFrame.h" +struct RowReflowState; + /** * nsTableRowFrame is the frame that maps table rows @@ -110,12 +112,60 @@ protected: /** protected constructor. * @see NewFrame */ - nsTableRowFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParentFrame); + nsTableRowFrame(nsIContent* aContent, + PRInt32 aIndexInParent, + nsIFrame* aParentFrame); + + /** destructor */ + virtual ~nsTableRowFrame(); + + nscoord GetTopMarginFor(nsIPresContext* aCX, + RowReflowState& aState, + nsStyleMolecule* aKidMol); + + void PlaceChild( nsIPresContext* aPresContext, + RowReflowState& aState, + nsIFrame* aKidFrame, + const nsRect& aKidRect, + nsSize* aMaxElementSize, + nsSize& aKidMaxElementSize); + + /** + * 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 ReflowMappedChildren(nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize); + + /** + * 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 PullUpChildren(nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize); + + /** + * Create new frames for content we haven't yet mapped + * + * @param aPresContext presentation context to use + * @param aState current inline state + * @return frComplete if all content has been mapped and frNotComplete + * if we should be continued + */ + ReflowStatus ReflowUnmappedChildren(nsIPresContext* aPresContext, + RowReflowState& aState, + nsSize* aMaxElementSize); - /** destructor */ - virtual ~nsTableRowFrame(); private: PRInt32 mTallestCell; // not my height, but the height of my tallest child diff --git a/mozilla/layout/tables/nsTableRowGroupFrame.cpp b/mozilla/layout/tables/nsTableRowGroupFrame.cpp index 06689af4349..e43975e4609 100644 --- a/mozilla/layout/tables/nsTableRowGroupFrame.cpp +++ b/mozilla/layout/tables/nsTableRowGroupFrame.cpp @@ -95,14 +95,24 @@ struct RowGroupReflowState { /* ----------- nsTableRowGroupFrame ---------- */ nsTableRowGroupFrame::nsTableRowGroupFrame(nsIContent* aContent, - PRInt32 aIndexInParent, - nsIFrame* aParentFrame) + PRInt32 aIndexInParent, + nsIFrame* aParentFrame) : nsContainerFrame(aContent, aIndexInParent, aParentFrame) { + mType = aContent->GetTag(); // mType: REFCNT++ } nsTableRowGroupFrame::~nsTableRowGroupFrame() { + if (nsnull!=mType) + NS_RELEASE(mType); // mType: REFCNT-- +} + +NS_METHOD nsTableRowGroupFrame::GetRowGroupType(nsIAtom *& aType) +{ + NS_ADDREF(mType); + aType=mType; + return NS_OK; } @@ -264,6 +274,8 @@ PRBool nsTableRowGroupFrame::ReflowMappedChildren( nsIPresContext* aPresCon for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) { nsSize kidAvailSize(aState.availSize); + if (0>=kidAvailSize.height) + kidAvailSize.height = 1; // XXX: HaCk - we don't handle negative heights yet nsReflowMetrics desiredSize; nsIFrame::ReflowStatus status; @@ -290,7 +302,7 @@ PRBool nsTableRowGroupFrame::ReflowMappedChildren( nsIPresContext* aPresCon // Reflow the child into the available space status = ReflowChild(kidFrame, aPresContext, desiredSize, - kidAvailSize, pKidMaxElementSize); + kidAvailSize, pKidMaxElementSize); // Did the child fit? if ((kidFrame != mFirstChild) && @@ -381,6 +393,12 @@ PRBool nsTableRowGroupFrame::ReflowMappedChildren( nsIPresContext* aPresCon break; } + // Add back in the left and right margins, because one row does not + // impact another row's width + if (PR_FALSE == aState.unconstrainedWidth) { + kidAvailSize.width += kidMol->margin.left + kidMol->margin.right; + } + // Get the next child kidFrame->GetNextSibling(kidFrame); @@ -868,10 +886,10 @@ nsTableRowGroupFrame::ResizeReflow( nsIPresContext* aPresContext, } // Return our desired rect - NS_ASSERTION(0