From cb664f26dac3ddea0e76a3dfffa812b8e3a2afba Mon Sep 17 00:00:00 2001 From: "buster%netscape.com" Date: Mon, 31 Aug 1998 21:23:28 +0000 Subject: [PATCH] reimplemented cell map in terms of nsVoidArrays for incremental building. the cellmap is now built incrementally, driven by nsTableRowFrame::InitialReflow git-svn-id: svn://10.0.0.236/trunk@8888 18797224-902f-48f8-a5cc-f745e15eee43 --- .../table/src/BasicTableLayoutStrategy.cpp | 6 +- .../html/table/src/BasicTableLayoutStrategy.h | 23 +- mozilla/layout/html/table/src/celldata.h | 4 + mozilla/layout/html/table/src/nsCellMap.cpp | 176 ++++++---- mozilla/layout/html/table/src/nsCellMap.h | 77 +++-- .../html/table/src/nsITableLayoutStrategy.h | 9 + .../html/table/src/nsTableColGroupFrame.cpp | 4 + .../layout/html/table/src/nsTableFrame.cpp | 301 +++++++++--------- mozilla/layout/html/table/src/nsTableFrame.h | 43 ++- .../layout/html/table/src/nsTableRowFrame.cpp | 61 ++-- .../tables/BasicTableLayoutStrategy.cpp | 6 +- .../layout/tables/BasicTableLayoutStrategy.h | 23 +- mozilla/layout/tables/celldata.h | 4 + mozilla/layout/tables/nsCellMap.cpp | 176 ++++++---- mozilla/layout/tables/nsCellMap.h | 77 +++-- .../layout/tables/nsITableLayoutStrategy.h | 9 + .../layout/tables/nsTableColGroupFrame.cpp | 4 + mozilla/layout/tables/nsTableFrame.cpp | 301 +++++++++--------- mozilla/layout/tables/nsTableFrame.h | 43 ++- mozilla/layout/tables/nsTableRowFrame.cpp | 61 ++-- 20 files changed, 880 insertions(+), 528 deletions(-) diff --git a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp index 1aed57a121e..64e8b90de5e 100644 --- a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp +++ b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.cpp @@ -321,7 +321,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths() maxColSpan = colSpan; if (colIndex!=cellFrame->GetColIndex()) { // For cells that span cols, we figure in the row using previously-built SpanInfo - NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match row span"); // sanity check + NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match col span"); // sanity check continue; } @@ -1251,6 +1251,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsTableFits(const nsReflowState& aR } tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset; } + if (1==mNumCols) + tableWidth += colInset; /* --- post-process if necessary --- */ @@ -1966,6 +1968,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsConstrained( const nsReflowState& } tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset; } + if (1==mNumCols) + tableWidth += colInset; /* --- post-process if necessary --- */ // first, assign autoWidth columns a width if (PR_TRUE==atLeastOneAutoWidthColumn) diff --git a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h index 726d8cf0093..efc59c43550 100644 --- a/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h +++ b/mozilla/layout/html/table/src/BasicTableLayoutStrategy.h @@ -29,6 +29,9 @@ struct nsStylePosition; /* ----------- SpanInfo ---------- */ +/** SpanInfo is a transient data structure that holds info about + * cells that have col spans. Used during column balancing. + */ struct SpanInfo { PRInt32 span; @@ -59,6 +62,11 @@ inline SpanInfo::SpanInfo(PRInt32 aColIndex, PRInt32 aSpan, /* ---------- BasicTableLayoutStrategy ---------- */ +/** Implementation of Nav4 compatible HTML browser table layout. + * The input to this class is the results from pass1 table layout. + * The output from this class is to set the column widths in + * mTableFrame. + */ class BasicTableLayoutStrategy : public nsITableLayoutStrategy { public: @@ -69,15 +77,25 @@ public: */ BasicTableLayoutStrategy(nsTableFrame *aFrame, PRInt32 aNumCols); - ~BasicTableLayoutStrategy(); + /** destructor */ + virtual ~BasicTableLayoutStrategy(); /** call once every time any table thing changes (content, structure, or style) */ virtual PRBool Initialize(nsSize* aMaxElementSize); + /** Called during resize reflow to determine the new column widths + * @param aTableStyle - the resolved style for mTableFrame + * @param aReflowState - the reflow state for mTableFrame + * @param aMaxWidth - the computed max width for columns to fit into + */ virtual PRBool BalanceColumnWidths(nsIStyleContext * aTableStyle, const nsReflowState& aReflowState, nscoord aMaxWidth); + nscoord GetTableMaxWidth() const; + +protected: + /** assign widths for each column. * if the column has a fixed coord width, use it. * if the column includes col spanning cells, @@ -235,5 +253,8 @@ protected: }; +inline nscoord BasicTableLayoutStrategy::GetTableMaxWidth() const +{ return mMaxTableWidth; }; + #endif diff --git a/mozilla/layout/html/table/src/celldata.h b/mozilla/layout/html/table/src/celldata.h index 37c519326bf..29134571b65 100644 --- a/mozilla/layout/html/table/src/celldata.h +++ b/mozilla/layout/html/table/src/celldata.h @@ -33,6 +33,10 @@ class nsTableCellFrame; class CellData { public: + /** if not null, the cell that this CellData maps. + * if null, mRealCell points to the CellData that holds the + * mapped cell frame. + */ nsTableCellFrame *mCell; CellData *mRealCell; CellData *mOverlap; diff --git a/mozilla/layout/html/table/src/nsCellMap.cpp b/mozilla/layout/html/table/src/nsCellMap.cpp index daba16d32b5..67b91b27888 100644 --- a/mozilla/layout/html/table/src/nsCellMap.cpp +++ b/mozilla/layout/html/table/src/nsCellMap.cpp @@ -16,121 +16,155 @@ * Reserved. */ -#include "nsCRT.h" #include "nsVoidArray.h" #include "nsCellMap.h" #include "nsTableFrame.h" #ifdef NS_DEBUG -static PRBool gsDebug1 = PR_FALSE; +static PRBool gsDebug = PR_FALSE; #else -static const PRBool gsDebug1 = PR_FALSE; +static const PRBool gsDebug = PR_FALSE; #endif -static const PRInt32 gBytesPerPointer = sizeof(PRInt32); - -nsCellMap::nsCellMap(int aRows, int aColumns) - : mRowCount(aRows), - mColCount(aColumns) +nsCellMap::nsCellMap(int aRowCount, int aColCount) + : mRowCount(0), + mColCount(0), + mTotalRowCount(0) { - mCells = nsnull; + mRows = nsnull; mColFrames = nsnull; mMinColSpans = nsnull; - Reset(aRows, aColumns); + Reset(aRowCount, aColCount); } nsCellMap::~nsCellMap() { - if (nsnull!=mCells) + if (nsnull!=mRows) { - for (int i=0;iElementAt(i)); + for (int j=mColCount; 0ElementAt(j)); if (data != nsnull) { delete data; - mCells[index] = 0; } } + delete row; } - delete [] mCells; + delete mRows; } if (nsnull != mColFrames) delete mColFrames; if (nsnull != mMinColSpans) delete [] mMinColSpans; - mCells = nsnull; + mRows = nsnull; mColFrames = nsnull; mMinColSpans = nsnull; }; -void nsCellMap::Reset(int aRows, int aColumns) +void nsCellMap::Reset(int aRowCount, int aColCount) { + if (gsDebug) printf("calling Reset(%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n", + aRowCount, aColCount, mRowCount, mColCount, mTotalRowCount); if (nsnull==mColFrames) { - mColFrames = new nsVoidArray(); + mColFrames = new nsVoidArray(); // don't give the array a count, because null col frames are illegal (unlike null cell entries in a row) } - // needs to be more efficient, to reuse space if possible - if (nsnull!=mCells) + if (nsnull==mRowCount) { - delete [] mCells; - mCells = nsnull; + mRows = new nsVoidArray(); // don't give the array a count, because null rows are illegal (unlike null cell entries in a row) } - mRowCount = aRows; - mColCount = aColumns; - mCells = new PRInt32 [mRowCount*mColCount*gBytesPerPointer]; - nsCRT::memset (mCells, 0, (mRowCount*mColCount)*gBytesPerPointer); -} - -void nsCellMap::GrowTo(int aColCount) -{ - if (aColCount <= mColCount) - return; - PRInt32 * newCells = new PRInt32 [mRowCount*aColCount*gBytesPerPointer]; - for (int rowIndex = 0; rowIndex < mRowCount; rowIndex++) + // void arrays force the caller to handle null padding elements themselves + // so if the number of columns has increased, we need to add extra cols to each row + PRInt32 newCols = mColCount-aColCount; + for (PRInt32 rowIndex=0; rowIndexElementAt(rowIndex)); + const PRInt32 colsInRow = row->Count(); + if (colsInRow == aColCount) + break; // we already have enough columns in each row + for (PRInt32 colIndex = colsInRow; colIndexAppendElement(nsnull); } - if (mCells != nsnull) - delete [] mCells; - mCells = newCells; + + // if the number of rows has increased, add the extra rows + PRInt32 newRows = aRowCount-mTotalRowCount; // (new row count) - (total row allocation) + for ( ; newRows>0; newRows--) + { + nsVoidArray *row; + if (0!=aColCount) + row = new nsVoidArray(aColCount); + else + row = new nsVoidArray(); + mRows->AppendElement(row); + } + + mRowCount = aRowCount; + mTotalRowCount = PR_MAX(mTotalRowCount, mRowCount); mColCount = aColCount; + if (gsDebug) printf("leaving Reset with mRC=%d, mCC=%d, mTRC=%d\n", + mRowCount, mColCount, mTotalRowCount); } void nsCellMap::DumpCellMap() const { - if (gsDebug1==PR_TRUE) + if (gsDebug==PR_TRUE) { printf("Cell Map =\n"); - for (int i=0;iElementAt(rowIndex)); + for (int colIndex=0; colIndexElementAt(colIndex)); + printf("Cell [%d,%d] = %p for index = %d\n", rowIndex, colIndex, data); } + } } } -void nsCellMap::SetCellAt(CellData *aCell, int aRow, int aColumn) +void nsCellMap::GrowToRow(PRInt32 aRowCount) { - //Assert aRow, aColumn - int index = (aRow*mColCount)+aColumn; - CellData* cell = GetCellAt(aRow,aColumn); - if (cell != nsnull) - delete cell; - mCells[index] = (PRInt32)aCell; + Reset(aRowCount, mColCount); } -nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex) +void nsCellMap::GrowToCol(PRInt32 aColCount) { + Reset(mRowCount, aColCount); +} + +void nsCellMap::SetCellAt(CellData *aCell, int aRowIndex, int aColIndex) +{ + NS_PRECONDITION(nsnull!=aCell, "bad aCell"); + PRInt32 newRows = (aRowIndex+1)-mRowCount; // add 1 to the "index" to get a "count" + if (00; newRows--) + { + nsVoidArray *row = new nsVoidArray(mColCount); + mRows->AppendElement(row); + } + mTotalRowCount = aRowIndex+1; // remember to always add 1 to an index when you want a count + } + + CellData* cell = GetCellAt(aRowIndex,aColIndex); + if (cell != nsnull) + delete cell; + nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex)); + row->ReplaceElementAt(aCell, aColIndex); + if (gsDebug) printf("leaving SetCellAt(%p,%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n", + aCell, aRowIndex, aColIndex, mRowCount, mColCount, mTotalRowCount); +} + +nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex) const +{ + NS_ASSERTION(nsnull!=mColFrames, "bad state"); return (nsTableColFrame *)(mColFrames->ElementAt(aColIndex)); } @@ -151,7 +185,7 @@ void nsCellMap::SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan) mMinColSpans[aColIndex] = aColSpan; } -PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex) +PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex) const { NS_ASSERTION(aColIndex mColCount) + { + result = aColIndex; + } + else + { + if (aRowIndex < mRowCount) + { + result = aColIndex; + nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex)); + PRInt32 count = row->Count(); + for (PRInt32 colIndex=aColIndex; colIndexElementAt(colIndex); + if (nsnull==data) + { + result = colIndex; + break; + } + result++; + } + } + } + return result; +} diff --git a/mozilla/layout/html/table/src/nsCellMap.h b/mozilla/layout/html/table/src/nsCellMap.h index 54dfdc51f0c..a461ba9bfa0 100644 --- a/mozilla/layout/html/table/src/nsCellMap.h +++ b/mozilla/layout/html/table/src/nsCellMap.h @@ -31,17 +31,18 @@ class nsTableCellFrame; * Each cell is represented by a CellData object. * * @see CellData - * @see nsTableFrame::BuildCellMap + * @see nsTableFrame::AddCellToMap * @see nsTableFrame::GrowCellMap * @see nsTableFrame::BuildCellIntoMap * - * acts like a 2-dimensional array, so all offsets are 0-indexed + * mRows is an array of rows. a row cannot be null. + * each row is an array of cells. a cell can be null. */ class nsCellMap { protected: - /** storage for CellData pointers */ - PRInt32 *mCells; ///XXX CellData *? + /** storage for rows */ + nsVoidArray *mRows; /** storage for CellData pointers */ PRInt32 *mMinColSpans; @@ -49,19 +50,28 @@ protected: /** a cache of the column frames, by col index */ nsVoidArray * mColFrames; - /** the number of rows */ - PRInt32 mRowCount; // in java, we could just do fCellMap.length; + /** the number of rows. mRows[0] - mRows[mRowCount-1] are non-null. */ + PRInt32 mRowCount; + + /** the number of rows allocated (due to cells having rowspans extending beyond the end of the table */ + PRInt32 mTotalRowCount; /** the number of columns (the max of all row lengths) */ PRInt32 mColCount; public: + /** constructor + * @param aRows - initial number of rows + * @param aColumns - initial number of columns + */ nsCellMap(PRInt32 aRows, PRInt32 aColumns); - // NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED + /** destructor + * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED + */ ~nsCellMap(); - /** initialize the CellMap to (aRows x aColumns) */ + /** set the CellMap to (aRows x aColumns) */ void Reset(PRInt32 aRows, PRInt32 aColumns); /** return the CellData for the cell at (aRowIndex,aColIndex) */ @@ -73,24 +83,49 @@ public: /** assign aCellData to the cell at (aRow,aColumn) */ void SetCellAt(CellData *aCellData, PRInt32 aRow, PRInt32 aColumn); + /** expand the CellMap to have aRowCount rows. The number of columns remains the same */ + void GrowToRow(PRInt32 aRowCount); + /** expand the CellMap to have aColCount columns. The number of rows remains the same */ - void GrowTo(PRInt32 aColCount); + void GrowToCol(PRInt32 aColCount); /** return the total number of columns in the table represented by this CellMap */ PRInt32 GetColCount() const; - /** return the total number of rows in the table represented by this CellMap */ + /** return the actual number of rows in the table represented by this CellMap */ PRInt32 GetRowCount() const; - nsTableColFrame * GetColumnFrame(PRInt32 aColIndex); + /** return the column frame associated with aColIndex */ + nsTableColFrame * GetColumnFrame(PRInt32 aColIndex) const; + /** return the index of the next column in aRowIndex after aColIndex + * that does not have a cell assigned to it. + * If aColIndex is past the end of the row, it is returned. + * If the row is not initialized in the cell map, 0 is returned. + */ + PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const; + + /** cache the min col span for all cells in aColIndex */ void SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan); - PRInt32 GetMinColSpan(PRInt32 aColIndex); + /** get the cached min col span for aColIndex */ + PRInt32 GetMinColSpan(PRInt32 aColIndex) const; + + /** add a column frame to the list of column frames + * column frames must be added in order + */ void AppendColumnFrame(nsTableColFrame *aColFrame); + /** return PR_TRUE if aRowIndex has any cells with rowspan>1 contained + * within it (not just cells that are in the row, but cells that span + * into the row as well. + */ PRBool RowImpactedBySpanningCell(PRInt32 aRowIndex); + /** return PR_TRUE if aColIndex has any cells with colspan>1 contained + * within it (not just cells that are in the col, but cells that span + * into the col as well. + */ PRBool ColumnImpactedBySpanningCell(PRInt32 aColIndex); /** for debugging */ @@ -102,16 +137,22 @@ public: inline CellData * nsCellMap::GetCellAt(PRInt32 aRowIndex, PRInt32 aColIndex) const { - NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg"); - NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg"); + NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg"); + NS_PRECONDITION(0<=aColIndex, "bad aColIndex arg"); + // don't check index vs. count for row or col, because it's ok to ask for a cell that doesn't yet exist + NS_PRECONDITION(nsnull!=mRows, "bad mRows"); - PRInt32 index = (aRowIndex*mColCount)+aColIndex; - return (CellData *)mCells[index]; + CellData *result = nsnull; + nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex)); + if (nsnull!=row) + result = (CellData *)(row->ElementAt(aColIndex)); + return result; } inline nsTableCellFrame * nsCellMap::GetCellFrameAt(PRInt32 aRowIndex, PRInt32 aColIndex) const { - NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg"); + NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg"); + // don't check aRowIndex vs. mRowCount, because it's ok to ask for a cell in a row that doesn't yet exist NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg"); nsTableCellFrame *result = nsnull; @@ -134,8 +175,6 @@ inline PRInt32 nsCellMap::GetRowCount() const inline void nsCellMap::AppendColumnFrame(nsTableColFrame *aColFrame) { mColFrames->AppendElement(aColFrame); - // sanity check - NS_ASSERTION(mColFrames->Count()<=mColCount, "too many columns appended to CellMap"); } diff --git a/mozilla/layout/html/table/src/nsITableLayoutStrategy.h b/mozilla/layout/html/table/src/nsITableLayoutStrategy.h index 543235dbf91..77bb1986c2c 100644 --- a/mozilla/layout/html/table/src/nsITableLayoutStrategy.h +++ b/mozilla/layout/html/table/src/nsITableLayoutStrategy.h @@ -44,6 +44,15 @@ public: virtual PRBool BalanceColumnWidths(nsIStyleContext *aTableStyle, const nsReflowState& aReflowState, nscoord aMaxWidth)=0; + + /** return the computed max "natural" size of the table. + * this is the sum of the desired size of the content taking into account table + * attributes, but NOT factoring in the available size the table is laying out into. + * the actual table width in a given situation will depend on the available size + * provided by the parent (especially for percent-width tables.) + */ + virtual nscoord GetTableMaxWidth() const = 0; + }; #endif diff --git a/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp b/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp index d2f7cd02537..b6f1910cdea 100644 --- a/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableColGroupFrame.cpp @@ -17,6 +17,7 @@ */ #include "nsTableColGroupFrame.h" #include "nsTableColFrame.h" +#include "nsTableFrame.h" #include "nsITableContent.h" #include "nsIReflowCommand.h" #include "nsIStyleContext.h" @@ -117,6 +118,9 @@ NS_METHOD nsTableColGroupFrame::Reflow(nsIPresContext& aPresContext, // set nsColFrame-specific information ((nsTableColFrame *)kidFrame)->SetColumnIndex(colIndex+mStartColIndex); + nsIFrame* tableFrame=nsnull; + GetGeometricParent(tableFrame); + ((nsTableFrame *)tableFrame)->AddColumnFrame((nsTableColFrame *)kidFrame); // Link child frame into the list of children if (nsnull != prevKidFrame) { diff --git a/mozilla/layout/html/table/src/nsTableFrame.cpp b/mozilla/layout/html/table/src/nsTableFrame.cpp index 495b5a12152..aae92adcfad 100644 --- a/mozilla/layout/html/table/src/nsTableFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableFrame.cpp @@ -517,12 +517,6 @@ void nsTableFrame::ResetCellMap () mCellMap = nsnull; // for now, will rebuild when needed } -/* call when column structure has changed. */ -void nsTableFrame::ResetColumns () -{ - EnsureCellMap(); -} - /** sum the columns represented by all nsTableColGroup objects * if the cell map says there are more columns than this, * add extra implicit columns to the content tree. @@ -533,7 +527,7 @@ void nsTableFrame::EnsureColumns(nsIPresContext* aPresContext, nsReflowStatus& aStatus) { // XXX sec should only be called on firstInFlow - EnsureCellMap(); + SetMinColSpanForTable(); if (nsnull==mCellMap) return; // no info yet, so nothing useful to do @@ -637,10 +631,6 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex, const nsReflowState& aReflowState, nsReflowStatus& aStatus) { - - if (nsnull!=mCellMap) - return; // we already have a cell map so this makes no sense - PRInt32 actualColumns = 0; nsTableColGroupFrame *lastColGroupFrame = nsnull; nsIFrame * firstRowGroupFrame=nsnull; @@ -724,6 +714,32 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex, } } +void nsTableFrame::AddColumnFrame (nsTableColFrame *aColFrame) +{ + mCellMap->AppendColumnFrame(aColFrame); +} + +/** return the index of the next row that is not yet assigned */ +PRInt32 nsTableFrame::GetNextAvailRowIndex() const +{ + PRInt32 result=0; + if (nsnull!=mCellMap) + { + result = mCellMap->GetRowCount(); // the next index is the current count + mCellMap->GrowToRow(result+1); // expand the cell map to include this new row + } + return result; +} + +/** return the index of the next column in aRowIndex that does not have a cell assigned to it */ +PRInt32 nsTableFrame::GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const +{ + PRInt32 result=0; + if (nsnull!=mCellMap) + result = mCellMap->GetNextAvailColIndex(aRowIndex, aColIndex); + return result; +} + /** Get the cell map for this table frame. It is not always mCellMap. * Only the firstInFlow has a legit cell map */ @@ -737,123 +753,10 @@ nsCellMap * nsTableFrame::GetCellMap() return mCellMap; } -void nsTableFrame::EnsureCellMap() +void nsTableFrame::SetMinColSpanForTable() { // XXX: must be called ONLY on first-in-flow - if (mCellMap == nsnull) - BuildCellMap(); -} - -void nsTableFrame::BuildCellMap () -{ - // XXX: must be called only on first-in-flow! - if (gsDebug==PR_TRUE) printf("Build Cell Map...\n"); - - int rowCount = GetRowCount(); - if (0 == rowCount) - { - // until we have some rows, there's nothing useful to do - return; - } - - // Make an educated guess as to how many columns we have. It's - // only a guess because we can't know exactly until we have - // processed the last row. - nsTableRowGroupFrame *rowGroupFrame = NextRowGroupFrame(nsnull); - if (0 == mColCount) - mColCount = GetSpecifiedColumnCount(); - if (0 == mColCount) // no column parts - { - // Use the first row to estimate the number of columns - nsTableRowFrame *rowFrame; - rowGroupFrame->FirstChild((nsIFrame*&)rowFrame); - if (nsnull!=rowFrame) - { - mColCount = rowFrame->GetMaxColumns(); - if (gsDebug==PR_TRUE) - printf("mColCount=0 at start. Guessing col count to be %d from a row.\n", mColCount); - } - } - - // If we have a cell map reset it; otherwise allocate a new cell map - if (nsnull==mCellMap) - mCellMap = new nsCellMap(rowCount, mColCount); - else - mCellMap->Reset(rowCount, mColCount); - if (gsDebug==PR_TRUE) printf("mCellMap set to (%d, %d)\n", rowCount, mColCount); - - // Iterate over each row group frame - PRInt32 rowIndex = -1; - while (nsnull != rowGroupFrame) - { - // Iterate over each row frame within the row group - nsTableRowFrame *rowFrame; - rowGroupFrame->FirstChild((nsIFrame*&)rowFrame); - while (nsnull != rowFrame) - { - // Set the row frame's row index. Note that this is a table-wide index, - // and not the index within the row group - rowIndex++; - rowFrame->SetRowIndex(rowIndex); - - // Iterate the table cells - PRInt32 cellIndex = 0; - PRInt32 colIndex = 0; - nsIFrame* cellFrame; - rowFrame->FirstChild(cellFrame); - - while ((nsnull != cellFrame) && (colIndex < mColCount)) - { - if (gsDebug==PR_TRUE) printf(" colIndex = %d, with mColCount = %d\n", colIndex, mColCount); - CellData *data = mCellMap->GetCellAt(rowIndex, colIndex); - if (nsnull == data) - { - BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex); - cellIndex++; - - // Get the next cell frame - cellFrame->GetNextSibling(cellFrame); - } - colIndex++; - } - - // See if there are any cell frames left in this row - if (nsnull != cellFrame) - { - // We didn't use all the cells in this row up. Grow the cell - // data because we now know that we have more columns than we - // originally thought we had. - PRInt32 cellCount = cellIndex + LengthOf(cellFrame); - - if (gsDebug==PR_TRUE) printf(" calling GrowCellMap because cellIndex < %d\n", cellIndex, cellCount); - GrowCellMap (cellCount); - while (nsnull != cellFrame) - { - if (gsDebug==PR_TRUE) printf(" calling GrowCellMap again because cellIndex < %d\n", cellIndex, cellCount); - GrowCellMap (colIndex + 1); // ensure enough cols in map, may be low due to colspans - CellData *data =mCellMap->GetCellAt(rowIndex, colIndex); - if (data == nsnull) - { - BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex); - cellIndex++; - - // Get the next cell frame - cellFrame->GetNextSibling(cellFrame); - } - colIndex++; - } - } - - // Get the next row frame - rowFrame->GetNextSibling((nsIFrame *&)rowFrame); - } - - // Get the next row group frame - rowGroupFrame = NextRowGroupFrame(rowGroupFrame); - } - if (gsDebug==PR_TRUE) - DumpCellMap (); - // iterate through the columns setting the min col span if necessary - // it would be more efficient if we could do this in the above loop + // set the minColSpan for each column + PRInt32 rowCount = mCellMap->GetRowCount(); for (PRInt32 colIndex=0; colIndexGetMaxColumns(); + } + + PRInt32 rowIndex; + // If we have a cell map reset it; otherwise allocate a new cell map + // also determine the index of aRowFrame and set it if necessary + if (0==mCellMap->GetRowCount()) + { // this is the first time we've ever been called + rowIndex = 0; + if (gsDebug==PR_TRUE) printf("rowFrame %p set to index %d\n", aRowFrame, rowIndex); + } + else + { + rowIndex = mCellMap->GetRowCount() - 1; // rowIndex is 0-indexed, rowCount is 1-indexed + } + + PRInt32 colIndex=0; + while (PR_TRUE) + { + CellData *data = mCellMap->GetCellAt(rowIndex, colIndex); + if (nsnull == data) + { + BuildCellIntoMap(aCellFrame, rowIndex, colIndex); + break; + } + colIndex++; + } + + if (gsDebug==PR_TRUE) + DumpCellMap (); +} + /** */ void nsTableFrame::DumpCellMap () @@ -882,7 +835,7 @@ void nsTableFrame::DumpCellMap () if (nsnull != mCellMap) { PRInt32 rowCount = mCellMap->GetRowCount(); - PRInt32 cols = GetColCount(); + PRInt32 cols = mCellMap->GetColCount(); for (PRInt32 r = 0; r < rowCount; r++) { if (gsDebug==PR_TRUE) @@ -933,29 +886,35 @@ void nsTableFrame::DumpCellMap () void nsTableFrame::BuildCellIntoMap (nsTableCellFrame *aCell, PRInt32 aRowIndex, PRInt32 aColIndex) { NS_PRECONDITION (nsnull!=aCell, "bad cell arg"); - NS_PRECONDITION (aColIndex < mColCount, "bad column index arg"); - NS_PRECONDITION (aRowIndex < GetRowCount(), "bad row index arg"); + NS_PRECONDITION (0 <= aColIndex, "bad column index arg"); + NS_PRECONDITION (0 <= aRowIndex, "bad row index arg"); // Setup CellMap for this cell - int rowSpan = GetEffectiveRowSpan (aRowIndex, aCell); - int colSpan = aCell->GetColSpan (); + int rowSpan = aCell->GetRowSpan(); + int colSpan = aCell->GetColSpan(); if (gsDebug==PR_TRUE) printf(" BuildCellIntoMap. rowSpan = %d, colSpan = %d\n", rowSpan, colSpan); // Grow the mCellMap array if we will end up addressing // some new columns. - if (mColCount < (aColIndex + colSpan)) + if (mCellMap->GetColCount() < (aColIndex + colSpan)) { - if (gsDebug==PR_TRUE) printf(" mColCount=%dGetRowCount() < (aRowIndex+1)) + { + printf("*********************************************** calling GrowToRow(%d)\n", aRowIndex+1); + mCellMap->GrowToRow(aRowIndex+1); + } + // Setup CellMap for this cell in the table CellData *data = new CellData (); data->mCell = aCell; data->mRealCell = data; if (gsDebug==PR_TRUE) printf(" calling mCellMap->SetCellAt(data, %d, %d)\n", aRowIndex, aColIndex); mCellMap->SetCellAt(data, aRowIndex, aColIndex); - aCell->SetColIndex (aColIndex); // Create CellData objects for the rows that this cell spans. Set // their mCell to nsnull and their mRealCell to point to data. If @@ -1000,10 +959,7 @@ void nsTableFrame::GrowCellMap (PRInt32 aColCount) { if (nsnull!=mCellMap) { - if (mColCount < aColCount) - { - mCellMap->GrowTo(aColCount); - } + mCellMap->GrowToCol(aColCount); mColCount = aColCount; } } @@ -1444,6 +1400,9 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext& aPresContext, { if (PR_FALSE==IsFirstPassValid()) { // we treat the table as if we've never seen the layout data before + if (mCellMap!=nsnull) + delete mCellMap; + mCellMap = new nsCellMap(0, 0); mPass = kPASS_FIRST; aStatus = ResizeReflowPass1(&aPresContext, aDesiredSize, aReflowState, aStatus); // check result @@ -1651,8 +1610,6 @@ nsReflowStatus nsTableFrame::ResizeReflowPass1(nsIPresContext* aPresContext, } } - // BuildColumnCache calls EnsureCellMap. If that ever changes, be sure to call EnsureCellMap - // here first. BuildColumnCache(aPresContext, aDesiredSize, aReflowState, aStatus); // Recalculate Layout Dependencies RecalcLayoutData(); @@ -1749,7 +1706,7 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext, } // Return our size and our status - aDesiredSize.width = aReflowState.maxSize.width; + aDesiredSize.width = ComputeDesiredWidth(aReflowState); aDesiredSize.height = state.y + myBorderPadding.top + myBorderPadding.bottom; @@ -1773,6 +1730,20 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext, } +nscoord nsTableFrame::ComputeDesiredWidth(const nsReflowState& aReflowState) const +{ + nscoord desiredWidth=aReflowState.maxSize.width; + // this is the biggest hack in the world. But there's no other rational way to handle nested percent tables + nsStylePosition* position; + PRBool isNested=IsNested(aReflowState, position); + if((eReflowReason_Initial==aReflowState.reason) && + (PR_TRUE==isNested) && (eStyleUnit_Percent==position->mWidth.GetUnit())) + { + desiredWidth = mTableLayoutStrategy->GetTableMaxWidth(); + } + return desiredWidth; +} + // Collapse child's top margin with previous bottom margin nscoord nsTableFrame::GetTopMarginFor(nsIPresContext* aCX, InnerTableReflowState& aState, @@ -2492,9 +2463,7 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, nsSize* aMaxElementSize) { NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!"); - - if (nsnull==mCellMap) - return; // we don't have any information yet, so we can't do any useful work + NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!"); PRInt32 numCols = GetColCount(); if (nsnull==mColumnWidths) @@ -2570,13 +2539,12 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, void nsTableFrame::SetTableWidth(nsIPresContext* aPresContext) { NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!"); + NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!"); nscoord cellSpacing = GetCellSpacing(); if (gsDebug==PR_TRUE) printf ("SetTableWidth with cellSpacing = %d ", cellSpacing); PRInt32 tableWidth = cellSpacing; - if (nsnull==mCellMap) - return; // no info, so nothing to do PRInt32 numCols = GetColCount(); for (PRInt32 colIndex = 0; colIndex aColIndex, "bad arg, col index out of bounds"); #endif @@ -2957,7 +2927,7 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth) { nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow(); NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow"); - //printf("SET_COL_WIDTH: %p, FIF=%p setting col %d to %d\n", this, firstInFlow, aColIndex, aWidth); + if (this!=firstInFlow) firstInFlow->SetColumnWidth(aColIndex, aWidth); else @@ -2968,9 +2938,6 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth) } } - - - /** * * Update the border style to map to the HTML border style @@ -3176,6 +3143,34 @@ nsresult nsTableFrame::NewFrame(nsIFrame** aInstancePtrResult, return NS_OK; } +/* helper method for determining if this is a nested table or not */ +PRBool nsTableFrame::IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const +{ + PRBool result = PR_FALSE; +#ifdef NS_DEBUG + PRInt32 counter=0; +#endif + // Walk up the reflow state chain until we find a cell or the root + const nsReflowState* rs = aReflowState.parentReflowState; + while (nsnull != rs) + { +#ifdef NS_DEBUG + counter++; + NS_ASSERTION(counter<100000, "infinite loop in IsNested"); +#endif + nsIFrame* parentTable = nsnull; + rs->frame->QueryInterface(kTableFrameCID, (void**) &parentTable); + if (nsnull!=parentTable) + { + result = PR_TRUE; + parentTable->GetStyleData(eStyleStruct_Position, ((nsStyleStruct *&)aPosition)); + break; + } + rs = rs->parentReflowState; + } + return result; +} + /* helper method for getting the width of the table's containing block */ nscoord nsTableFrame::GetTableContainerWidth(const nsReflowState& aReflowState) { diff --git a/mozilla/layout/html/table/src/nsTableFrame.h b/mozilla/layout/html/table/src/nsTableFrame.h index a913c8fdb43..fdc8caafbad 100644 --- a/mozilla/layout/html/table/src/nsTableFrame.h +++ b/mozilla/layout/html/table/src/nsTableFrame.h @@ -73,6 +73,9 @@ public: // nsISupports NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); + /** helper method for determining if this is a nested table or not */ + PRBool IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const; + /** helper method for getting the width of the table's containing block */ static nscoord GetTableContainerWidth(const nsReflowState& aState); @@ -210,6 +213,26 @@ public: const nsReflowState& aReflowState, nsReflowStatus& aStatus); + /** return the index of the next row that is not yet assigned. + * If no row is initialized, 0 is returned. + */ + PRInt32 GetNextAvailRowIndex() const; + + /** return the index of the next column in aRowIndex after aColIndex + * that does not have a cell assigned to it. + * If aColIndex is past the end of the row, it is returned. + * If the row is not initialized, 0 is returned. + */ + PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const; + + /** build as much of the CellMap as possible from the info we have so far + */ + virtual void AddCellToTable (nsTableRowFrame *aRowFrame, + nsTableCellFrame *aCellFrame, + PRBool aAddRow); + + virtual void AddColumnFrame (nsTableColFrame *aColFrame); + protected: /** protected constructor. @@ -256,6 +279,11 @@ protected: nsIFrame* aKidFrame, nscoord aDeltaY); + /** return the desired width of this table accounting for the current + * reflow state, and for the table attributes and parent + */ + nscoord ComputeDesiredWidth(const nsReflowState& aReflowState) const; + nscoord GetTopMarginFor(nsIPresContext* aCX, InnerTableReflowState& aState, const nsMargin& aKidMargin); @@ -345,10 +373,6 @@ protected: void MapHTMLBorderStyle(nsStyleSpacing& aSpacingStyle, nscoord aBorderWidth); PRBool ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult); - /** build as much of the CellMap as possible from the info we have so far - */ - virtual void BuildCellMap (); - /** called whenever the number of columns changes, to increase the storage in mCellMap */ virtual void GrowCellMap(PRInt32 aColCount); @@ -374,12 +398,6 @@ protected: */ void ListColumnLayoutData(FILE* out, PRInt32 aIndent); - /** ResetColumns is called when the column structure of the table is changed. - * Call with caution, only when adding or removing columns, changing - * column attributes, changing the rowspan or colspan attribute of a cell, etc. - */ - virtual void ResetColumns (); - /** sum the columns represented by all nsTableColGroup objects. * if the cell map says there are more columns than this, * add extra implicit columns to the content tree. @@ -389,9 +407,8 @@ protected: const nsReflowState& aReflowState, nsReflowStatus& aStatus); - /** Ensure that the cell map has been built for the table - */ - virtual void EnsureCellMap(); + /** Set the min col span for every column in the table. Scans the whole table. */ + virtual void SetMinColSpanForTable(); virtual void BuildColumnCache(nsIPresContext* aPresContext, nsReflowMetrics& aDesiredSize, diff --git a/mozilla/layout/html/table/src/nsTableRowFrame.cpp b/mozilla/layout/html/table/src/nsTableRowFrame.cpp index bb8b72bc7f3..7ba1f2d7680 100644 --- a/mozilla/layout/html/table/src/nsTableRowFrame.cpp +++ b/mozilla/layout/html/table/src/nsTableRowFrame.cpp @@ -31,6 +31,11 @@ #include "nsIPtr.h" #include "nsIReflowCommand.h" #include "nsCSSRendering.h" +// the following header files are required for style optimizations that work only when the child content is really a cell +#include "nsITableContent.h" +#include "nsTableCell.h" +static NS_DEFINE_IID(kITableContentIID, NS_ITABLECONTENT_IID); +// end includes for style optimizations that require real content knowledge NS_DEF_PTR(nsIStyleContext); @@ -502,12 +507,13 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, // Place our children, one at a time, until we are out of children nsSize kidMaxElementSize; PRInt32 kidIndex = 0; - PRInt32 colIndex = -1; + PRInt32 colIndex = 0; nsIFrame* prevKidFrame = nsnull; nscoord maxTopMargin = 0; nscoord maxBottomMargin = 0; nscoord x = 0; nsresult result = NS_OK; + PRBool isFirst=PR_TRUE; for (;;) { // Get the next content object @@ -517,22 +523,41 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, break; // no more content } - // what column does this cell span to? - nsHTMLValue value; - ((nsHTMLTagContent *)cell)->GetAttribute(nsHTMLAtoms::align, value); - if (value.GetUnit() == eHTMLUnit_Integer) - colIndex += value.GetIntValue(); - else - colIndex += 1; + // what row am I? + if (PR_TRUE==isFirst) + SetRowIndex(aState.tableFrame->GetNextAvailRowIndex()); - // create column frames if necessary + // what column does this cell belong to? + colIndex = aState.tableFrame->GetNextAvailColIndex(mRowIndex, colIndex); + if (gsDebug) printf("%p : next col index = %d\n", this, colIndex); + + /* for style context optimization, set the content's column index if possible. + * this can only be done if we really have an nsTableCell. + * other tags mapped to table cell display won't benefit from this optimization + * see nsHTMLStyleSheet::RulesMatching + */ + nsITableContent *tableContentInterface = nsnull; + nsresult rv = cell->QueryInterface(kITableContentIID, + (void **)&tableContentInterface); // tableContentInterface: REFCNT++ + if (NS_SUCCEEDED(rv)) + { // we know it's a table part of some sort, is it a cell? + const int contentType = ((nsTableContent *)tableContentInterface)->GetType(); + if (contentType == nsITableContent::kTableCellType) + { + ((nsTableCell *)tableContentInterface)->SetColIndex(colIndex); + if (gsDebug) printf("%p : set cell content %p to col index = %d\n", this, tableContentInterface, colIndex); + } + NS_RELEASE(tableContentInterface); + } + // part of the style optimization is to ensure that the column frame for the cell exists + // we used to do this post-pass1, now we do it incrementally for the optimization nsReflowStatus status; aState.tableFrame->EnsureColumnFrameAt(colIndex, &aPresContext, aDesiredSize, aState.reflowState, status); - // Create a child frame -- always an nsTableCell frame + // Create a child frame -- always an nsTableCellFrame nsIStyleContext* kidSC = aPresContext.ResolveStyleContextFor(cell, this, PR_FALSE); nsIContentDelegate* kidDel = cell->GetDelegate(&aPresContext); nsIFrame* kidFrame; @@ -544,21 +569,16 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, NS_RELEASE(kidSC); break; } + // this sets the frame's notion of it's column index + ((nsTableCellFrame *)kidFrame)->SetColIndex(colIndex); + if (gsDebug) printf("%p : set cell frame %p to col index = %d\n", this, kidFrame, colIndex); + // add the cell frame to the table's cell map + aState.tableFrame->AddCellToTable(this, (nsTableCellFrame *)kidFrame, isFirst); // Because we're not splittable always allow the child to be as high as // it wants. The default available width is also unconstrained so we can // get the child's maximum width nsSize kidAvailSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - - // See if there's a width constraint for the cell - /* - const nsStylePosition* cellPosition = (const nsStylePosition*) - kidSC->GetStyleData(eStyleStruct_Position); - if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit()) - { - kidAvailSize.width = cellPosition->mWidth.GetCoordValue(); - } - */ NS_IF_RELEASE(kidSC); // Get the child's margins @@ -622,6 +642,7 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, // Move to the next content child prevKidFrame = kidFrame; kidIndex++; + isFirst=PR_FALSE; } SetMaxChildHeight(aState.maxCellHeight, maxTopMargin, maxBottomMargin); // remember height of tallest child who doesn't have a row span diff --git a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp index 1aed57a121e..64e8b90de5e 100644 --- a/mozilla/layout/tables/BasicTableLayoutStrategy.cpp +++ b/mozilla/layout/tables/BasicTableLayoutStrategy.cpp @@ -321,7 +321,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths() maxColSpan = colSpan; if (colIndex!=cellFrame->GetColIndex()) { // For cells that span cols, we figure in the row using previously-built SpanInfo - NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match row span"); // sanity check + NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match col span"); // sanity check continue; } @@ -1251,6 +1251,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsTableFits(const nsReflowState& aR } tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset; } + if (1==mNumCols) + tableWidth += colInset; /* --- post-process if necessary --- */ @@ -1966,6 +1968,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsConstrained( const nsReflowState& } tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset; } + if (1==mNumCols) + tableWidth += colInset; /* --- post-process if necessary --- */ // first, assign autoWidth columns a width if (PR_TRUE==atLeastOneAutoWidthColumn) diff --git a/mozilla/layout/tables/BasicTableLayoutStrategy.h b/mozilla/layout/tables/BasicTableLayoutStrategy.h index 726d8cf0093..efc59c43550 100644 --- a/mozilla/layout/tables/BasicTableLayoutStrategy.h +++ b/mozilla/layout/tables/BasicTableLayoutStrategy.h @@ -29,6 +29,9 @@ struct nsStylePosition; /* ----------- SpanInfo ---------- */ +/** SpanInfo is a transient data structure that holds info about + * cells that have col spans. Used during column balancing. + */ struct SpanInfo { PRInt32 span; @@ -59,6 +62,11 @@ inline SpanInfo::SpanInfo(PRInt32 aColIndex, PRInt32 aSpan, /* ---------- BasicTableLayoutStrategy ---------- */ +/** Implementation of Nav4 compatible HTML browser table layout. + * The input to this class is the results from pass1 table layout. + * The output from this class is to set the column widths in + * mTableFrame. + */ class BasicTableLayoutStrategy : public nsITableLayoutStrategy { public: @@ -69,15 +77,25 @@ public: */ BasicTableLayoutStrategy(nsTableFrame *aFrame, PRInt32 aNumCols); - ~BasicTableLayoutStrategy(); + /** destructor */ + virtual ~BasicTableLayoutStrategy(); /** call once every time any table thing changes (content, structure, or style) */ virtual PRBool Initialize(nsSize* aMaxElementSize); + /** Called during resize reflow to determine the new column widths + * @param aTableStyle - the resolved style for mTableFrame + * @param aReflowState - the reflow state for mTableFrame + * @param aMaxWidth - the computed max width for columns to fit into + */ virtual PRBool BalanceColumnWidths(nsIStyleContext * aTableStyle, const nsReflowState& aReflowState, nscoord aMaxWidth); + nscoord GetTableMaxWidth() const; + +protected: + /** assign widths for each column. * if the column has a fixed coord width, use it. * if the column includes col spanning cells, @@ -235,5 +253,8 @@ protected: }; +inline nscoord BasicTableLayoutStrategy::GetTableMaxWidth() const +{ return mMaxTableWidth; }; + #endif diff --git a/mozilla/layout/tables/celldata.h b/mozilla/layout/tables/celldata.h index 37c519326bf..29134571b65 100644 --- a/mozilla/layout/tables/celldata.h +++ b/mozilla/layout/tables/celldata.h @@ -33,6 +33,10 @@ class nsTableCellFrame; class CellData { public: + /** if not null, the cell that this CellData maps. + * if null, mRealCell points to the CellData that holds the + * mapped cell frame. + */ nsTableCellFrame *mCell; CellData *mRealCell; CellData *mOverlap; diff --git a/mozilla/layout/tables/nsCellMap.cpp b/mozilla/layout/tables/nsCellMap.cpp index daba16d32b5..67b91b27888 100644 --- a/mozilla/layout/tables/nsCellMap.cpp +++ b/mozilla/layout/tables/nsCellMap.cpp @@ -16,121 +16,155 @@ * Reserved. */ -#include "nsCRT.h" #include "nsVoidArray.h" #include "nsCellMap.h" #include "nsTableFrame.h" #ifdef NS_DEBUG -static PRBool gsDebug1 = PR_FALSE; +static PRBool gsDebug = PR_FALSE; #else -static const PRBool gsDebug1 = PR_FALSE; +static const PRBool gsDebug = PR_FALSE; #endif -static const PRInt32 gBytesPerPointer = sizeof(PRInt32); - -nsCellMap::nsCellMap(int aRows, int aColumns) - : mRowCount(aRows), - mColCount(aColumns) +nsCellMap::nsCellMap(int aRowCount, int aColCount) + : mRowCount(0), + mColCount(0), + mTotalRowCount(0) { - mCells = nsnull; + mRows = nsnull; mColFrames = nsnull; mMinColSpans = nsnull; - Reset(aRows, aColumns); + Reset(aRowCount, aColCount); } nsCellMap::~nsCellMap() { - if (nsnull!=mCells) + if (nsnull!=mRows) { - for (int i=0;iElementAt(i)); + for (int j=mColCount; 0ElementAt(j)); if (data != nsnull) { delete data; - mCells[index] = 0; } } + delete row; } - delete [] mCells; + delete mRows; } if (nsnull != mColFrames) delete mColFrames; if (nsnull != mMinColSpans) delete [] mMinColSpans; - mCells = nsnull; + mRows = nsnull; mColFrames = nsnull; mMinColSpans = nsnull; }; -void nsCellMap::Reset(int aRows, int aColumns) +void nsCellMap::Reset(int aRowCount, int aColCount) { + if (gsDebug) printf("calling Reset(%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n", + aRowCount, aColCount, mRowCount, mColCount, mTotalRowCount); if (nsnull==mColFrames) { - mColFrames = new nsVoidArray(); + mColFrames = new nsVoidArray(); // don't give the array a count, because null col frames are illegal (unlike null cell entries in a row) } - // needs to be more efficient, to reuse space if possible - if (nsnull!=mCells) + if (nsnull==mRowCount) { - delete [] mCells; - mCells = nsnull; + mRows = new nsVoidArray(); // don't give the array a count, because null rows are illegal (unlike null cell entries in a row) } - mRowCount = aRows; - mColCount = aColumns; - mCells = new PRInt32 [mRowCount*mColCount*gBytesPerPointer]; - nsCRT::memset (mCells, 0, (mRowCount*mColCount)*gBytesPerPointer); -} - -void nsCellMap::GrowTo(int aColCount) -{ - if (aColCount <= mColCount) - return; - PRInt32 * newCells = new PRInt32 [mRowCount*aColCount*gBytesPerPointer]; - for (int rowIndex = 0; rowIndex < mRowCount; rowIndex++) + // void arrays force the caller to handle null padding elements themselves + // so if the number of columns has increased, we need to add extra cols to each row + PRInt32 newCols = mColCount-aColCount; + for (PRInt32 rowIndex=0; rowIndexElementAt(rowIndex)); + const PRInt32 colsInRow = row->Count(); + if (colsInRow == aColCount) + break; // we already have enough columns in each row + for (PRInt32 colIndex = colsInRow; colIndexAppendElement(nsnull); } - if (mCells != nsnull) - delete [] mCells; - mCells = newCells; + + // if the number of rows has increased, add the extra rows + PRInt32 newRows = aRowCount-mTotalRowCount; // (new row count) - (total row allocation) + for ( ; newRows>0; newRows--) + { + nsVoidArray *row; + if (0!=aColCount) + row = new nsVoidArray(aColCount); + else + row = new nsVoidArray(); + mRows->AppendElement(row); + } + + mRowCount = aRowCount; + mTotalRowCount = PR_MAX(mTotalRowCount, mRowCount); mColCount = aColCount; + if (gsDebug) printf("leaving Reset with mRC=%d, mCC=%d, mTRC=%d\n", + mRowCount, mColCount, mTotalRowCount); } void nsCellMap::DumpCellMap() const { - if (gsDebug1==PR_TRUE) + if (gsDebug==PR_TRUE) { printf("Cell Map =\n"); - for (int i=0;iElementAt(rowIndex)); + for (int colIndex=0; colIndexElementAt(colIndex)); + printf("Cell [%d,%d] = %p for index = %d\n", rowIndex, colIndex, data); } + } } } -void nsCellMap::SetCellAt(CellData *aCell, int aRow, int aColumn) +void nsCellMap::GrowToRow(PRInt32 aRowCount) { - //Assert aRow, aColumn - int index = (aRow*mColCount)+aColumn; - CellData* cell = GetCellAt(aRow,aColumn); - if (cell != nsnull) - delete cell; - mCells[index] = (PRInt32)aCell; + Reset(aRowCount, mColCount); } -nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex) +void nsCellMap::GrowToCol(PRInt32 aColCount) { + Reset(mRowCount, aColCount); +} + +void nsCellMap::SetCellAt(CellData *aCell, int aRowIndex, int aColIndex) +{ + NS_PRECONDITION(nsnull!=aCell, "bad aCell"); + PRInt32 newRows = (aRowIndex+1)-mRowCount; // add 1 to the "index" to get a "count" + if (00; newRows--) + { + nsVoidArray *row = new nsVoidArray(mColCount); + mRows->AppendElement(row); + } + mTotalRowCount = aRowIndex+1; // remember to always add 1 to an index when you want a count + } + + CellData* cell = GetCellAt(aRowIndex,aColIndex); + if (cell != nsnull) + delete cell; + nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex)); + row->ReplaceElementAt(aCell, aColIndex); + if (gsDebug) printf("leaving SetCellAt(%p,%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n", + aCell, aRowIndex, aColIndex, mRowCount, mColCount, mTotalRowCount); +} + +nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex) const +{ + NS_ASSERTION(nsnull!=mColFrames, "bad state"); return (nsTableColFrame *)(mColFrames->ElementAt(aColIndex)); } @@ -151,7 +185,7 @@ void nsCellMap::SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan) mMinColSpans[aColIndex] = aColSpan; } -PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex) +PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex) const { NS_ASSERTION(aColIndex mColCount) + { + result = aColIndex; + } + else + { + if (aRowIndex < mRowCount) + { + result = aColIndex; + nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex)); + PRInt32 count = row->Count(); + for (PRInt32 colIndex=aColIndex; colIndexElementAt(colIndex); + if (nsnull==data) + { + result = colIndex; + break; + } + result++; + } + } + } + return result; +} diff --git a/mozilla/layout/tables/nsCellMap.h b/mozilla/layout/tables/nsCellMap.h index 54dfdc51f0c..a461ba9bfa0 100644 --- a/mozilla/layout/tables/nsCellMap.h +++ b/mozilla/layout/tables/nsCellMap.h @@ -31,17 +31,18 @@ class nsTableCellFrame; * Each cell is represented by a CellData object. * * @see CellData - * @see nsTableFrame::BuildCellMap + * @see nsTableFrame::AddCellToMap * @see nsTableFrame::GrowCellMap * @see nsTableFrame::BuildCellIntoMap * - * acts like a 2-dimensional array, so all offsets are 0-indexed + * mRows is an array of rows. a row cannot be null. + * each row is an array of cells. a cell can be null. */ class nsCellMap { protected: - /** storage for CellData pointers */ - PRInt32 *mCells; ///XXX CellData *? + /** storage for rows */ + nsVoidArray *mRows; /** storage for CellData pointers */ PRInt32 *mMinColSpans; @@ -49,19 +50,28 @@ protected: /** a cache of the column frames, by col index */ nsVoidArray * mColFrames; - /** the number of rows */ - PRInt32 mRowCount; // in java, we could just do fCellMap.length; + /** the number of rows. mRows[0] - mRows[mRowCount-1] are non-null. */ + PRInt32 mRowCount; + + /** the number of rows allocated (due to cells having rowspans extending beyond the end of the table */ + PRInt32 mTotalRowCount; /** the number of columns (the max of all row lengths) */ PRInt32 mColCount; public: + /** constructor + * @param aRows - initial number of rows + * @param aColumns - initial number of columns + */ nsCellMap(PRInt32 aRows, PRInt32 aColumns); - // NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED + /** destructor + * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED + */ ~nsCellMap(); - /** initialize the CellMap to (aRows x aColumns) */ + /** set the CellMap to (aRows x aColumns) */ void Reset(PRInt32 aRows, PRInt32 aColumns); /** return the CellData for the cell at (aRowIndex,aColIndex) */ @@ -73,24 +83,49 @@ public: /** assign aCellData to the cell at (aRow,aColumn) */ void SetCellAt(CellData *aCellData, PRInt32 aRow, PRInt32 aColumn); + /** expand the CellMap to have aRowCount rows. The number of columns remains the same */ + void GrowToRow(PRInt32 aRowCount); + /** expand the CellMap to have aColCount columns. The number of rows remains the same */ - void GrowTo(PRInt32 aColCount); + void GrowToCol(PRInt32 aColCount); /** return the total number of columns in the table represented by this CellMap */ PRInt32 GetColCount() const; - /** return the total number of rows in the table represented by this CellMap */ + /** return the actual number of rows in the table represented by this CellMap */ PRInt32 GetRowCount() const; - nsTableColFrame * GetColumnFrame(PRInt32 aColIndex); + /** return the column frame associated with aColIndex */ + nsTableColFrame * GetColumnFrame(PRInt32 aColIndex) const; + /** return the index of the next column in aRowIndex after aColIndex + * that does not have a cell assigned to it. + * If aColIndex is past the end of the row, it is returned. + * If the row is not initialized in the cell map, 0 is returned. + */ + PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const; + + /** cache the min col span for all cells in aColIndex */ void SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan); - PRInt32 GetMinColSpan(PRInt32 aColIndex); + /** get the cached min col span for aColIndex */ + PRInt32 GetMinColSpan(PRInt32 aColIndex) const; + + /** add a column frame to the list of column frames + * column frames must be added in order + */ void AppendColumnFrame(nsTableColFrame *aColFrame); + /** return PR_TRUE if aRowIndex has any cells with rowspan>1 contained + * within it (not just cells that are in the row, but cells that span + * into the row as well. + */ PRBool RowImpactedBySpanningCell(PRInt32 aRowIndex); + /** return PR_TRUE if aColIndex has any cells with colspan>1 contained + * within it (not just cells that are in the col, but cells that span + * into the col as well. + */ PRBool ColumnImpactedBySpanningCell(PRInt32 aColIndex); /** for debugging */ @@ -102,16 +137,22 @@ public: inline CellData * nsCellMap::GetCellAt(PRInt32 aRowIndex, PRInt32 aColIndex) const { - NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg"); - NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg"); + NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg"); + NS_PRECONDITION(0<=aColIndex, "bad aColIndex arg"); + // don't check index vs. count for row or col, because it's ok to ask for a cell that doesn't yet exist + NS_PRECONDITION(nsnull!=mRows, "bad mRows"); - PRInt32 index = (aRowIndex*mColCount)+aColIndex; - return (CellData *)mCells[index]; + CellData *result = nsnull; + nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex)); + if (nsnull!=row) + result = (CellData *)(row->ElementAt(aColIndex)); + return result; } inline nsTableCellFrame * nsCellMap::GetCellFrameAt(PRInt32 aRowIndex, PRInt32 aColIndex) const { - NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg"); + NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg"); + // don't check aRowIndex vs. mRowCount, because it's ok to ask for a cell in a row that doesn't yet exist NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg"); nsTableCellFrame *result = nsnull; @@ -134,8 +175,6 @@ inline PRInt32 nsCellMap::GetRowCount() const inline void nsCellMap::AppendColumnFrame(nsTableColFrame *aColFrame) { mColFrames->AppendElement(aColFrame); - // sanity check - NS_ASSERTION(mColFrames->Count()<=mColCount, "too many columns appended to CellMap"); } diff --git a/mozilla/layout/tables/nsITableLayoutStrategy.h b/mozilla/layout/tables/nsITableLayoutStrategy.h index 543235dbf91..77bb1986c2c 100644 --- a/mozilla/layout/tables/nsITableLayoutStrategy.h +++ b/mozilla/layout/tables/nsITableLayoutStrategy.h @@ -44,6 +44,15 @@ public: virtual PRBool BalanceColumnWidths(nsIStyleContext *aTableStyle, const nsReflowState& aReflowState, nscoord aMaxWidth)=0; + + /** return the computed max "natural" size of the table. + * this is the sum of the desired size of the content taking into account table + * attributes, but NOT factoring in the available size the table is laying out into. + * the actual table width in a given situation will depend on the available size + * provided by the parent (especially for percent-width tables.) + */ + virtual nscoord GetTableMaxWidth() const = 0; + }; #endif diff --git a/mozilla/layout/tables/nsTableColGroupFrame.cpp b/mozilla/layout/tables/nsTableColGroupFrame.cpp index d2f7cd02537..b6f1910cdea 100644 --- a/mozilla/layout/tables/nsTableColGroupFrame.cpp +++ b/mozilla/layout/tables/nsTableColGroupFrame.cpp @@ -17,6 +17,7 @@ */ #include "nsTableColGroupFrame.h" #include "nsTableColFrame.h" +#include "nsTableFrame.h" #include "nsITableContent.h" #include "nsIReflowCommand.h" #include "nsIStyleContext.h" @@ -117,6 +118,9 @@ NS_METHOD nsTableColGroupFrame::Reflow(nsIPresContext& aPresContext, // set nsColFrame-specific information ((nsTableColFrame *)kidFrame)->SetColumnIndex(colIndex+mStartColIndex); + nsIFrame* tableFrame=nsnull; + GetGeometricParent(tableFrame); + ((nsTableFrame *)tableFrame)->AddColumnFrame((nsTableColFrame *)kidFrame); // Link child frame into the list of children if (nsnull != prevKidFrame) { diff --git a/mozilla/layout/tables/nsTableFrame.cpp b/mozilla/layout/tables/nsTableFrame.cpp index 495b5a12152..aae92adcfad 100644 --- a/mozilla/layout/tables/nsTableFrame.cpp +++ b/mozilla/layout/tables/nsTableFrame.cpp @@ -517,12 +517,6 @@ void nsTableFrame::ResetCellMap () mCellMap = nsnull; // for now, will rebuild when needed } -/* call when column structure has changed. */ -void nsTableFrame::ResetColumns () -{ - EnsureCellMap(); -} - /** sum the columns represented by all nsTableColGroup objects * if the cell map says there are more columns than this, * add extra implicit columns to the content tree. @@ -533,7 +527,7 @@ void nsTableFrame::EnsureColumns(nsIPresContext* aPresContext, nsReflowStatus& aStatus) { // XXX sec should only be called on firstInFlow - EnsureCellMap(); + SetMinColSpanForTable(); if (nsnull==mCellMap) return; // no info yet, so nothing useful to do @@ -637,10 +631,6 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex, const nsReflowState& aReflowState, nsReflowStatus& aStatus) { - - if (nsnull!=mCellMap) - return; // we already have a cell map so this makes no sense - PRInt32 actualColumns = 0; nsTableColGroupFrame *lastColGroupFrame = nsnull; nsIFrame * firstRowGroupFrame=nsnull; @@ -724,6 +714,32 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex, } } +void nsTableFrame::AddColumnFrame (nsTableColFrame *aColFrame) +{ + mCellMap->AppendColumnFrame(aColFrame); +} + +/** return the index of the next row that is not yet assigned */ +PRInt32 nsTableFrame::GetNextAvailRowIndex() const +{ + PRInt32 result=0; + if (nsnull!=mCellMap) + { + result = mCellMap->GetRowCount(); // the next index is the current count + mCellMap->GrowToRow(result+1); // expand the cell map to include this new row + } + return result; +} + +/** return the index of the next column in aRowIndex that does not have a cell assigned to it */ +PRInt32 nsTableFrame::GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const +{ + PRInt32 result=0; + if (nsnull!=mCellMap) + result = mCellMap->GetNextAvailColIndex(aRowIndex, aColIndex); + return result; +} + /** Get the cell map for this table frame. It is not always mCellMap. * Only the firstInFlow has a legit cell map */ @@ -737,123 +753,10 @@ nsCellMap * nsTableFrame::GetCellMap() return mCellMap; } -void nsTableFrame::EnsureCellMap() +void nsTableFrame::SetMinColSpanForTable() { // XXX: must be called ONLY on first-in-flow - if (mCellMap == nsnull) - BuildCellMap(); -} - -void nsTableFrame::BuildCellMap () -{ - // XXX: must be called only on first-in-flow! - if (gsDebug==PR_TRUE) printf("Build Cell Map...\n"); - - int rowCount = GetRowCount(); - if (0 == rowCount) - { - // until we have some rows, there's nothing useful to do - return; - } - - // Make an educated guess as to how many columns we have. It's - // only a guess because we can't know exactly until we have - // processed the last row. - nsTableRowGroupFrame *rowGroupFrame = NextRowGroupFrame(nsnull); - if (0 == mColCount) - mColCount = GetSpecifiedColumnCount(); - if (0 == mColCount) // no column parts - { - // Use the first row to estimate the number of columns - nsTableRowFrame *rowFrame; - rowGroupFrame->FirstChild((nsIFrame*&)rowFrame); - if (nsnull!=rowFrame) - { - mColCount = rowFrame->GetMaxColumns(); - if (gsDebug==PR_TRUE) - printf("mColCount=0 at start. Guessing col count to be %d from a row.\n", mColCount); - } - } - - // If we have a cell map reset it; otherwise allocate a new cell map - if (nsnull==mCellMap) - mCellMap = new nsCellMap(rowCount, mColCount); - else - mCellMap->Reset(rowCount, mColCount); - if (gsDebug==PR_TRUE) printf("mCellMap set to (%d, %d)\n", rowCount, mColCount); - - // Iterate over each row group frame - PRInt32 rowIndex = -1; - while (nsnull != rowGroupFrame) - { - // Iterate over each row frame within the row group - nsTableRowFrame *rowFrame; - rowGroupFrame->FirstChild((nsIFrame*&)rowFrame); - while (nsnull != rowFrame) - { - // Set the row frame's row index. Note that this is a table-wide index, - // and not the index within the row group - rowIndex++; - rowFrame->SetRowIndex(rowIndex); - - // Iterate the table cells - PRInt32 cellIndex = 0; - PRInt32 colIndex = 0; - nsIFrame* cellFrame; - rowFrame->FirstChild(cellFrame); - - while ((nsnull != cellFrame) && (colIndex < mColCount)) - { - if (gsDebug==PR_TRUE) printf(" colIndex = %d, with mColCount = %d\n", colIndex, mColCount); - CellData *data = mCellMap->GetCellAt(rowIndex, colIndex); - if (nsnull == data) - { - BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex); - cellIndex++; - - // Get the next cell frame - cellFrame->GetNextSibling(cellFrame); - } - colIndex++; - } - - // See if there are any cell frames left in this row - if (nsnull != cellFrame) - { - // We didn't use all the cells in this row up. Grow the cell - // data because we now know that we have more columns than we - // originally thought we had. - PRInt32 cellCount = cellIndex + LengthOf(cellFrame); - - if (gsDebug==PR_TRUE) printf(" calling GrowCellMap because cellIndex < %d\n", cellIndex, cellCount); - GrowCellMap (cellCount); - while (nsnull != cellFrame) - { - if (gsDebug==PR_TRUE) printf(" calling GrowCellMap again because cellIndex < %d\n", cellIndex, cellCount); - GrowCellMap (colIndex + 1); // ensure enough cols in map, may be low due to colspans - CellData *data =mCellMap->GetCellAt(rowIndex, colIndex); - if (data == nsnull) - { - BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex); - cellIndex++; - - // Get the next cell frame - cellFrame->GetNextSibling(cellFrame); - } - colIndex++; - } - } - - // Get the next row frame - rowFrame->GetNextSibling((nsIFrame *&)rowFrame); - } - - // Get the next row group frame - rowGroupFrame = NextRowGroupFrame(rowGroupFrame); - } - if (gsDebug==PR_TRUE) - DumpCellMap (); - // iterate through the columns setting the min col span if necessary - // it would be more efficient if we could do this in the above loop + // set the minColSpan for each column + PRInt32 rowCount = mCellMap->GetRowCount(); for (PRInt32 colIndex=0; colIndexGetMaxColumns(); + } + + PRInt32 rowIndex; + // If we have a cell map reset it; otherwise allocate a new cell map + // also determine the index of aRowFrame and set it if necessary + if (0==mCellMap->GetRowCount()) + { // this is the first time we've ever been called + rowIndex = 0; + if (gsDebug==PR_TRUE) printf("rowFrame %p set to index %d\n", aRowFrame, rowIndex); + } + else + { + rowIndex = mCellMap->GetRowCount() - 1; // rowIndex is 0-indexed, rowCount is 1-indexed + } + + PRInt32 colIndex=0; + while (PR_TRUE) + { + CellData *data = mCellMap->GetCellAt(rowIndex, colIndex); + if (nsnull == data) + { + BuildCellIntoMap(aCellFrame, rowIndex, colIndex); + break; + } + colIndex++; + } + + if (gsDebug==PR_TRUE) + DumpCellMap (); +} + /** */ void nsTableFrame::DumpCellMap () @@ -882,7 +835,7 @@ void nsTableFrame::DumpCellMap () if (nsnull != mCellMap) { PRInt32 rowCount = mCellMap->GetRowCount(); - PRInt32 cols = GetColCount(); + PRInt32 cols = mCellMap->GetColCount(); for (PRInt32 r = 0; r < rowCount; r++) { if (gsDebug==PR_TRUE) @@ -933,29 +886,35 @@ void nsTableFrame::DumpCellMap () void nsTableFrame::BuildCellIntoMap (nsTableCellFrame *aCell, PRInt32 aRowIndex, PRInt32 aColIndex) { NS_PRECONDITION (nsnull!=aCell, "bad cell arg"); - NS_PRECONDITION (aColIndex < mColCount, "bad column index arg"); - NS_PRECONDITION (aRowIndex < GetRowCount(), "bad row index arg"); + NS_PRECONDITION (0 <= aColIndex, "bad column index arg"); + NS_PRECONDITION (0 <= aRowIndex, "bad row index arg"); // Setup CellMap for this cell - int rowSpan = GetEffectiveRowSpan (aRowIndex, aCell); - int colSpan = aCell->GetColSpan (); + int rowSpan = aCell->GetRowSpan(); + int colSpan = aCell->GetColSpan(); if (gsDebug==PR_TRUE) printf(" BuildCellIntoMap. rowSpan = %d, colSpan = %d\n", rowSpan, colSpan); // Grow the mCellMap array if we will end up addressing // some new columns. - if (mColCount < (aColIndex + colSpan)) + if (mCellMap->GetColCount() < (aColIndex + colSpan)) { - if (gsDebug==PR_TRUE) printf(" mColCount=%dGetRowCount() < (aRowIndex+1)) + { + printf("*********************************************** calling GrowToRow(%d)\n", aRowIndex+1); + mCellMap->GrowToRow(aRowIndex+1); + } + // Setup CellMap for this cell in the table CellData *data = new CellData (); data->mCell = aCell; data->mRealCell = data; if (gsDebug==PR_TRUE) printf(" calling mCellMap->SetCellAt(data, %d, %d)\n", aRowIndex, aColIndex); mCellMap->SetCellAt(data, aRowIndex, aColIndex); - aCell->SetColIndex (aColIndex); // Create CellData objects for the rows that this cell spans. Set // their mCell to nsnull and their mRealCell to point to data. If @@ -1000,10 +959,7 @@ void nsTableFrame::GrowCellMap (PRInt32 aColCount) { if (nsnull!=mCellMap) { - if (mColCount < aColCount) - { - mCellMap->GrowTo(aColCount); - } + mCellMap->GrowToCol(aColCount); mColCount = aColCount; } } @@ -1444,6 +1400,9 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext& aPresContext, { if (PR_FALSE==IsFirstPassValid()) { // we treat the table as if we've never seen the layout data before + if (mCellMap!=nsnull) + delete mCellMap; + mCellMap = new nsCellMap(0, 0); mPass = kPASS_FIRST; aStatus = ResizeReflowPass1(&aPresContext, aDesiredSize, aReflowState, aStatus); // check result @@ -1651,8 +1610,6 @@ nsReflowStatus nsTableFrame::ResizeReflowPass1(nsIPresContext* aPresContext, } } - // BuildColumnCache calls EnsureCellMap. If that ever changes, be sure to call EnsureCellMap - // here first. BuildColumnCache(aPresContext, aDesiredSize, aReflowState, aStatus); // Recalculate Layout Dependencies RecalcLayoutData(); @@ -1749,7 +1706,7 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext, } // Return our size and our status - aDesiredSize.width = aReflowState.maxSize.width; + aDesiredSize.width = ComputeDesiredWidth(aReflowState); aDesiredSize.height = state.y + myBorderPadding.top + myBorderPadding.bottom; @@ -1773,6 +1730,20 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext, } +nscoord nsTableFrame::ComputeDesiredWidth(const nsReflowState& aReflowState) const +{ + nscoord desiredWidth=aReflowState.maxSize.width; + // this is the biggest hack in the world. But there's no other rational way to handle nested percent tables + nsStylePosition* position; + PRBool isNested=IsNested(aReflowState, position); + if((eReflowReason_Initial==aReflowState.reason) && + (PR_TRUE==isNested) && (eStyleUnit_Percent==position->mWidth.GetUnit())) + { + desiredWidth = mTableLayoutStrategy->GetTableMaxWidth(); + } + return desiredWidth; +} + // Collapse child's top margin with previous bottom margin nscoord nsTableFrame::GetTopMarginFor(nsIPresContext* aCX, InnerTableReflowState& aState, @@ -2492,9 +2463,7 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, nsSize* aMaxElementSize) { NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!"); - - if (nsnull==mCellMap) - return; // we don't have any information yet, so we can't do any useful work + NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!"); PRInt32 numCols = GetColCount(); if (nsnull==mColumnWidths) @@ -2570,13 +2539,12 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext, void nsTableFrame::SetTableWidth(nsIPresContext* aPresContext) { NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!"); + NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!"); nscoord cellSpacing = GetCellSpacing(); if (gsDebug==PR_TRUE) printf ("SetTableWidth with cellSpacing = %d ", cellSpacing); PRInt32 tableWidth = cellSpacing; - if (nsnull==mCellMap) - return; // no info, so nothing to do PRInt32 numCols = GetColCount(); for (PRInt32 colIndex = 0; colIndex aColIndex, "bad arg, col index out of bounds"); #endif @@ -2957,7 +2927,7 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth) { nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow(); NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow"); - //printf("SET_COL_WIDTH: %p, FIF=%p setting col %d to %d\n", this, firstInFlow, aColIndex, aWidth); + if (this!=firstInFlow) firstInFlow->SetColumnWidth(aColIndex, aWidth); else @@ -2968,9 +2938,6 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth) } } - - - /** * * Update the border style to map to the HTML border style @@ -3176,6 +3143,34 @@ nsresult nsTableFrame::NewFrame(nsIFrame** aInstancePtrResult, return NS_OK; } +/* helper method for determining if this is a nested table or not */ +PRBool nsTableFrame::IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const +{ + PRBool result = PR_FALSE; +#ifdef NS_DEBUG + PRInt32 counter=0; +#endif + // Walk up the reflow state chain until we find a cell or the root + const nsReflowState* rs = aReflowState.parentReflowState; + while (nsnull != rs) + { +#ifdef NS_DEBUG + counter++; + NS_ASSERTION(counter<100000, "infinite loop in IsNested"); +#endif + nsIFrame* parentTable = nsnull; + rs->frame->QueryInterface(kTableFrameCID, (void**) &parentTable); + if (nsnull!=parentTable) + { + result = PR_TRUE; + parentTable->GetStyleData(eStyleStruct_Position, ((nsStyleStruct *&)aPosition)); + break; + } + rs = rs->parentReflowState; + } + return result; +} + /* helper method for getting the width of the table's containing block */ nscoord nsTableFrame::GetTableContainerWidth(const nsReflowState& aReflowState) { diff --git a/mozilla/layout/tables/nsTableFrame.h b/mozilla/layout/tables/nsTableFrame.h index a913c8fdb43..fdc8caafbad 100644 --- a/mozilla/layout/tables/nsTableFrame.h +++ b/mozilla/layout/tables/nsTableFrame.h @@ -73,6 +73,9 @@ public: // nsISupports NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr); + /** helper method for determining if this is a nested table or not */ + PRBool IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const; + /** helper method for getting the width of the table's containing block */ static nscoord GetTableContainerWidth(const nsReflowState& aState); @@ -210,6 +213,26 @@ public: const nsReflowState& aReflowState, nsReflowStatus& aStatus); + /** return the index of the next row that is not yet assigned. + * If no row is initialized, 0 is returned. + */ + PRInt32 GetNextAvailRowIndex() const; + + /** return the index of the next column in aRowIndex after aColIndex + * that does not have a cell assigned to it. + * If aColIndex is past the end of the row, it is returned. + * If the row is not initialized, 0 is returned. + */ + PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const; + + /** build as much of the CellMap as possible from the info we have so far + */ + virtual void AddCellToTable (nsTableRowFrame *aRowFrame, + nsTableCellFrame *aCellFrame, + PRBool aAddRow); + + virtual void AddColumnFrame (nsTableColFrame *aColFrame); + protected: /** protected constructor. @@ -256,6 +279,11 @@ protected: nsIFrame* aKidFrame, nscoord aDeltaY); + /** return the desired width of this table accounting for the current + * reflow state, and for the table attributes and parent + */ + nscoord ComputeDesiredWidth(const nsReflowState& aReflowState) const; + nscoord GetTopMarginFor(nsIPresContext* aCX, InnerTableReflowState& aState, const nsMargin& aKidMargin); @@ -345,10 +373,6 @@ protected: void MapHTMLBorderStyle(nsStyleSpacing& aSpacingStyle, nscoord aBorderWidth); PRBool ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult); - /** build as much of the CellMap as possible from the info we have so far - */ - virtual void BuildCellMap (); - /** called whenever the number of columns changes, to increase the storage in mCellMap */ virtual void GrowCellMap(PRInt32 aColCount); @@ -374,12 +398,6 @@ protected: */ void ListColumnLayoutData(FILE* out, PRInt32 aIndent); - /** ResetColumns is called when the column structure of the table is changed. - * Call with caution, only when adding or removing columns, changing - * column attributes, changing the rowspan or colspan attribute of a cell, etc. - */ - virtual void ResetColumns (); - /** sum the columns represented by all nsTableColGroup objects. * if the cell map says there are more columns than this, * add extra implicit columns to the content tree. @@ -389,9 +407,8 @@ protected: const nsReflowState& aReflowState, nsReflowStatus& aStatus); - /** Ensure that the cell map has been built for the table - */ - virtual void EnsureCellMap(); + /** Set the min col span for every column in the table. Scans the whole table. */ + virtual void SetMinColSpanForTable(); virtual void BuildColumnCache(nsIPresContext* aPresContext, nsReflowMetrics& aDesiredSize, diff --git a/mozilla/layout/tables/nsTableRowFrame.cpp b/mozilla/layout/tables/nsTableRowFrame.cpp index bb8b72bc7f3..7ba1f2d7680 100644 --- a/mozilla/layout/tables/nsTableRowFrame.cpp +++ b/mozilla/layout/tables/nsTableRowFrame.cpp @@ -31,6 +31,11 @@ #include "nsIPtr.h" #include "nsIReflowCommand.h" #include "nsCSSRendering.h" +// the following header files are required for style optimizations that work only when the child content is really a cell +#include "nsITableContent.h" +#include "nsTableCell.h" +static NS_DEFINE_IID(kITableContentIID, NS_ITABLECONTENT_IID); +// end includes for style optimizations that require real content knowledge NS_DEF_PTR(nsIStyleContext); @@ -502,12 +507,13 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, // Place our children, one at a time, until we are out of children nsSize kidMaxElementSize; PRInt32 kidIndex = 0; - PRInt32 colIndex = -1; + PRInt32 colIndex = 0; nsIFrame* prevKidFrame = nsnull; nscoord maxTopMargin = 0; nscoord maxBottomMargin = 0; nscoord x = 0; nsresult result = NS_OK; + PRBool isFirst=PR_TRUE; for (;;) { // Get the next content object @@ -517,22 +523,41 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, break; // no more content } - // what column does this cell span to? - nsHTMLValue value; - ((nsHTMLTagContent *)cell)->GetAttribute(nsHTMLAtoms::align, value); - if (value.GetUnit() == eHTMLUnit_Integer) - colIndex += value.GetIntValue(); - else - colIndex += 1; + // what row am I? + if (PR_TRUE==isFirst) + SetRowIndex(aState.tableFrame->GetNextAvailRowIndex()); - // create column frames if necessary + // what column does this cell belong to? + colIndex = aState.tableFrame->GetNextAvailColIndex(mRowIndex, colIndex); + if (gsDebug) printf("%p : next col index = %d\n", this, colIndex); + + /* for style context optimization, set the content's column index if possible. + * this can only be done if we really have an nsTableCell. + * other tags mapped to table cell display won't benefit from this optimization + * see nsHTMLStyleSheet::RulesMatching + */ + nsITableContent *tableContentInterface = nsnull; + nsresult rv = cell->QueryInterface(kITableContentIID, + (void **)&tableContentInterface); // tableContentInterface: REFCNT++ + if (NS_SUCCEEDED(rv)) + { // we know it's a table part of some sort, is it a cell? + const int contentType = ((nsTableContent *)tableContentInterface)->GetType(); + if (contentType == nsITableContent::kTableCellType) + { + ((nsTableCell *)tableContentInterface)->SetColIndex(colIndex); + if (gsDebug) printf("%p : set cell content %p to col index = %d\n", this, tableContentInterface, colIndex); + } + NS_RELEASE(tableContentInterface); + } + // part of the style optimization is to ensure that the column frame for the cell exists + // we used to do this post-pass1, now we do it incrementally for the optimization nsReflowStatus status; aState.tableFrame->EnsureColumnFrameAt(colIndex, &aPresContext, aDesiredSize, aState.reflowState, status); - // Create a child frame -- always an nsTableCell frame + // Create a child frame -- always an nsTableCellFrame nsIStyleContext* kidSC = aPresContext.ResolveStyleContextFor(cell, this, PR_FALSE); nsIContentDelegate* kidDel = cell->GetDelegate(&aPresContext); nsIFrame* kidFrame; @@ -544,21 +569,16 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, NS_RELEASE(kidSC); break; } + // this sets the frame's notion of it's column index + ((nsTableCellFrame *)kidFrame)->SetColIndex(colIndex); + if (gsDebug) printf("%p : set cell frame %p to col index = %d\n", this, kidFrame, colIndex); + // add the cell frame to the table's cell map + aState.tableFrame->AddCellToTable(this, (nsTableCellFrame *)kidFrame, isFirst); // Because we're not splittable always allow the child to be as high as // it wants. The default available width is also unconstrained so we can // get the child's maximum width nsSize kidAvailSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); - - // See if there's a width constraint for the cell - /* - const nsStylePosition* cellPosition = (const nsStylePosition*) - kidSC->GetStyleData(eStyleStruct_Position); - if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit()) - { - kidAvailSize.width = cellPosition->mWidth.GetCoordValue(); - } - */ NS_IF_RELEASE(kidSC); // Get the child's margins @@ -622,6 +642,7 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext, // Move to the next content child prevKidFrame = kidFrame; kidIndex++; + isFirst=PR_FALSE; } SetMaxChildHeight(aState.maxCellHeight, maxTopMargin, maxBottomMargin); // remember height of tallest child who doesn't have a row span