diff options
Diffstat (limited to 'layout/tables/nsCellMap.h')
-rw-r--r-- | layout/tables/nsCellMap.h | 575 |
1 files changed, 575 insertions, 0 deletions
diff --git a/layout/tables/nsCellMap.h b/layout/tables/nsCellMap.h new file mode 100644 index 0000000000..20bec0d07d --- /dev/null +++ b/layout/tables/nsCellMap.h @@ -0,0 +1,575 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#ifndef nsCellMap_h__ +#define nsCellMap_h__ + +#include "nscore.h" +#include "celldata.h" +#include "nsTArray.h" +#include "nsCOMPtr.h" +#include "nsAlgorithm.h" +#include "nsRect.h" +#include <algorithm> +#include "TableArea.h" + +#undef DEBUG_TABLE_CELLMAP + +class nsTableCellFrame; +class nsTableRowFrame; +class nsTableRowGroupFrame; +class nsTableFrame; +class nsCellMap; +class nsPresContext; +class nsCellMapColumnIterator; + +struct nsColInfo { + int32_t mNumCellsOrig; // number of cells originating in the col + int32_t mNumCellsSpan; // number of cells spanning into the col via colspans + // (not rowspans) + + nsColInfo(); + nsColInfo(int32_t aNumCellsOrig, int32_t aNumCellsSpan); +}; + +struct BCInfo { + nsTArray<BCData> mIEndBorders; + nsTArray<BCData> mBEndBorders; + BCData mBEndIEndCorner; +}; + +class nsTableCellMap { + typedef mozilla::TableArea TableArea; + + public: + nsTableCellMap(nsTableFrame& aTableFrame, bool aBorderCollapse); + + /** destructor + * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED + */ + ~nsTableCellMap(); + + void RemoveGroupCellMap(nsTableRowGroupFrame* aRowGroup); + + void InsertGroupCellMap(nsTableRowGroupFrame* aNewRowGroup, + nsTableRowGroupFrame*& aPrevRowGroup); + + /** + * Get the nsCellMap for the given row group. If aStartHint is non-null, + * will start looking with that cellmap and only fall back to starting at the + * beginning of the list if that doesn't find us the right nsCellMap. + * Otherwise, just start at the beginning. + * + * aRowGroup must not be null. + */ + nsCellMap* GetMapFor(const nsTableRowGroupFrame* aRowGroup, + nsCellMap* aStartHint) const; + + /** synchronize the cellmaps with the rowgroups again **/ + void Synchronize(nsTableFrame* aTableFrame); + + nsTableCellFrame* GetCellFrame(int32_t aRowIndex, int32_t aColIndex, + CellData& aData, bool aUseRowIfOverlap) const; + + /** return the CellData for the cell at (aRowIndex, aColIndex) */ + CellData* GetDataAt(int32_t aRowIndex, int32_t aColIndex) const; + + // this function creates a col if needed + nsColInfo* GetColInfoAt(int32_t aColIndex); + + /** append the cellFrame at the end of the row at aRowIndex and return the col + * index + */ + CellData* AppendCell(nsTableCellFrame& aCellFrame, int32_t aRowIndex, + bool aRebuildIfNecessary, TableArea& aDamageArea); + + void InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames, int32_t aRowIndex, + int32_t aColIndexBefore, TableArea& aDamageArea); + + void RemoveCell(nsTableCellFrame* aCellFrame, int32_t aRowIndex, + TableArea& aDamageArea); + /** Remove the previously gathered column information */ + void ClearCols(); + void InsertRows(nsTableRowGroupFrame* aRowGroup, + nsTArray<nsTableRowFrame*>& aRows, int32_t aFirstRowIndex, + bool aConsiderSpans, TableArea& aDamageArea); + + void RemoveRows(int32_t aFirstRowIndex, int32_t aNumRowsToRemove, + bool aConsiderSpans, TableArea& aDamageArea); + + int32_t GetNumCellsOriginatingInRow(int32_t aRowIndex) const; + int32_t GetNumCellsOriginatingInCol(int32_t aColIndex) const; + + /** indicate whether the row has more than one cell that either originates + * or is spanned from the rows above + */ + bool HasMoreThanOneCell(int32_t aRowIndex) const; + + int32_t GetEffectiveRowSpan(int32_t aRowIndex, int32_t aColIndex) const; + int32_t GetEffectiveColSpan(int32_t aRowIndex, int32_t aColIndex) const; + + /** return the total number of columns in the table represented by this + * CellMap */ + int32_t GetColCount() const; + + /** return the actual number of rows in the table represented by this CellMap + */ + int32_t GetRowCount() const; + + nsTableCellFrame* GetCellInfoAt(int32_t aRowX, int32_t aColX, + bool* aOriginates = nullptr, + int32_t* aColSpan = nullptr) const; + + /** + * Returns the index at the given row and column coordinates. + * + * @see nsITableLayout::GetIndexByRowAndColumn() + * + * @param aRow [in] the row coordinate + * @param aColumn [in] the column coordinate + * @returns the index for the cell + */ + int32_t GetIndexByRowAndColumn(int32_t aRow, int32_t aColumn) const; + + /** + * Retrieves the row and column coordinates for the given index. + * + * @see nsITableLayout::GetRowAndColumnByIndex() + * + * @param aIndex [in] the index for which coordinates are to be retrieved + * @param aRow [out] the row coordinate to be returned + * @param aColumn [out] the column coordinate to be returned + */ + void GetRowAndColumnByIndex(int32_t aIndex, int32_t* aRow, + int32_t* aColumn) const; + + void AddColsAtEnd(uint32_t aNumCols); + void RemoveColsAtEnd(); + + bool RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) const; + bool RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) const; + void RebuildConsideringCells(nsCellMap* aCellMap, + nsTArray<nsTableCellFrame*>* aCellFrames, + int32_t aRowIndex, int32_t aColIndex, + bool aInsert, TableArea& aDamageArea); + + protected: + /** + * Rebuild due to rows being inserted or deleted with cells spanning + * into or out of the rows. This function can only handle insertion + * or deletion but NOT both. So either aRowsToInsert must be null + * or aNumRowsToRemove must be 0. + * + * // XXXbz are both allowed to happen? That'd be a no-op... + */ + void RebuildConsideringRows(nsCellMap* aCellMap, int32_t aStartRowIndex, + nsTArray<nsTableRowFrame*>* aRowsToInsert, + int32_t aNumRowsToRemove, TableArea& aDamageArea); + + public: + void ResetBStartStart(mozilla::LogicalSide aSide, nsCellMap& aCellMap, + uint32_t aRowGroupStart, uint32_t aYPos, + uint32_t aXPos); + + void SetBCBorderEdge(mozilla::LogicalSide aEdge, nsCellMap& aCellMap, + uint32_t aCellMapStart, uint32_t aYPos, uint32_t aXPos, + uint32_t aLength, BCBorderOwner aOwner, nscoord aSize, + bool aChanged); + + void SetBCBorderCorner(mozilla::LogicalCorner aCorner, nsCellMap& aCellMap, + uint32_t aCellMapStart, uint32_t aYPos, uint32_t aXPos, + mozilla::LogicalSide aOwner, nscoord aSubSize, + bool aBevel, bool aIsBottomRight = false); + + /** dump a representation of the cell map to stdout for debugging */ +#ifdef DEBUG + void Dump(char* aString = nullptr) const; +#endif + + protected: + BCData* GetIEndMostBorder(int32_t aRowIndex); + BCData* GetBEndMostBorder(int32_t aColIndex); + + friend class nsCellMap; + friend class BCMapCellIterator; + friend class BCPaintBorderIterator; + friend class nsCellMapColumnIterator; + + /** Insert a row group cellmap after aPrevMap, if aPrefMap is null insert it + * at the beginning, the ordering of the cellmap corresponds to the ordering + * of rowgroups once OrderRowGroups has been called + */ + void InsertGroupCellMap(nsCellMap* aPrevMap, nsCellMap& aNewMap); + void DeleteIEndBEndBorders(); + + nsTableFrame& mTableFrame; + AutoTArray<nsColInfo, 8> mCols; + nsCellMap* mFirstMap; + // border collapsing info + BCInfo* mBCInfo; +}; + +/** nsCellMap is a support class for nsTablePart. + * It maintains an Rows x Columns grid onto which the cells of the table are + * mapped. This makes processing of rowspan and colspan attributes much easier. + * Each cell is represented by a CellData object. + * + * @see CellData + * @see nsTableFrame::AddCellToMap + * @see nsTableFrame::GrowCellMap + * @see nsTableFrame::BuildCellIntoMap + * + * mRows is an array of rows. Each row is an array of cells. a cell + * can be null. + */ +class nsCellMap { + typedef mozilla::TableArea TableArea; + + public: + /** constructor + * @param aRowGroupFrame the row group frame this is a cellmap for + * @param aIsBC whether the table is doing border-collapse + */ + nsCellMap(nsTableRowGroupFrame* aRowGroupFrame, bool aIsBC); + + /** destructor + * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED + */ + ~nsCellMap(); + + static void Init(); + static void Shutdown(); + + nsCellMap* GetNextSibling() const; + void SetNextSibling(nsCellMap* aSibling); + + nsTableRowGroupFrame* GetRowGroup() const; + + nsTableCellFrame* GetCellFrame(int32_t aRowIndex, int32_t aColIndex, + CellData& aData, + bool aUseRowSpanIfOverlap) const; + + /** + * Returns highest cell index within the cell map. + * + * @param aColCount [in] the number of columns in the table + */ + int32_t GetHighestIndex(int32_t aColCount); + + /** + * Returns the index of the given row and column coordinates. + * + * @see nsITableLayout::GetIndexByRowAndColumn() + * + * @param aColCount [in] the number of columns in the table + * @param aRow [in] the row coordinate + * @param aColumn [in] the column coordinate + */ + int32_t GetIndexByRowAndColumn(int32_t aColCount, int32_t aRow, + int32_t aColumn) const; + + /** + * Get the row and column coordinates at the given index. + * + * @see nsITableLayout::GetRowAndColumnByIndex() + * + * @param aColCount [in] the number of columns in the table + * @param aIndex [in] the index for which coordinates are to be retrieved + * @param aRow [out] the row coordinate to be returned + * @param aColumn [out] the column coordinate to be returned + */ + void GetRowAndColumnByIndex(int32_t aColCount, int32_t aIndex, int32_t* aRow, + int32_t* aColumn) const; + + /** append the cellFrame at an empty or dead cell or finally at the end of + * the row at aRowIndex and return a pointer to the celldata entry in the + * cellmap + * + * @param aMap - reference to the table cell map + * @param aCellFrame - a pointer to the cellframe which will be + * appended to the row + * @param aRowIndex - to this row the celldata entry will be added + * @param aRebuildIfNecessay - if a cell spans into a row below it might be + * necesserary to rebuild the cellmap as this + * rowspan might overlap another cell. + * @param aDamageArea - area in cellmap coordinates which have been + * updated. + * @param aColToBeginSearch - if not null contains the column number where + * the search for a empty or dead cell in the + * row should start + * @return - a pointer to the celldata entry inserted into + * the cellmap + */ + CellData* AppendCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, + int32_t aRowIndex, bool aRebuildIfNecessary, + int32_t aRgFirstRowIndex, TableArea& aDamageArea, + int32_t* aBeginSearchAtCol = nullptr); + + void InsertCells(nsTableCellMap& aMap, + nsTArray<nsTableCellFrame*>& aCellFrames, int32_t aRowIndex, + int32_t aColIndexBefore, int32_t aRgFirstRowIndex, + TableArea& aDamageArea); + + void RemoveCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, + int32_t aRowIndex, int32_t aRgFirstRowIndex, + TableArea& aDamageArea); + + void InsertRows(nsTableCellMap& aMap, nsTArray<nsTableRowFrame*>& aRows, + int32_t aFirstRowIndex, bool aConsiderSpans, + int32_t aRgFirstRowIndex, TableArea& aDamageArea); + + void RemoveRows(nsTableCellMap& aMap, int32_t aFirstRowIndex, + int32_t aNumRowsToRemove, bool aConsiderSpans, + int32_t aRgFirstRowIndex, TableArea& aDamageArea); + + int32_t GetNumCellsOriginatingInRow(int32_t aRowIndex) const; + int32_t GetNumCellsOriginatingInCol(int32_t aColIndex) const; + + /** return the number of rows in the table represented by this CellMap */ + int32_t GetRowCount(bool aConsiderDeadRowSpanRows = false) const; + + nsTableCellFrame* GetCellInfoAt(const nsTableCellMap& aMap, int32_t aRowX, + int32_t aColX, bool* aOriginates = nullptr, + int32_t* aColSpan = nullptr) const; + + bool RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) const; + + bool RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) const; + + /** indicate whether the row has more than one cell that either originates + * or is spanned from the rows above + */ + bool HasMoreThanOneCell(int32_t aRowIndex) const; + + /* Get the rowspan for a cell starting at aRowIndex and aColIndex. + * If aGetEffective is true the size will not exceed the last content based + * row. Cells can have a specified rowspan that extends below the last + * content based row. This is legitimate considering incr. reflow where the + * content rows will arive later. + */ + int32_t GetRowSpan(int32_t aRowIndex, int32_t aColIndex, + bool aGetEffective) const; + + int32_t GetEffectiveColSpan(const nsTableCellMap& aMap, int32_t aRowIndex, + int32_t aColIndex) const; + + typedef nsTArray<CellData*> CellDataArray; + + /** dump a representation of the cell map to stdout for debugging */ +#ifdef DEBUG + void Dump(bool aIsBorderCollapse) const; +#endif + + protected: + friend class nsTableCellMap; + friend class BCMapCellIterator; + friend class BCPaintBorderIterator; + friend class nsTableFrame; + friend class nsCellMapColumnIterator; + + /** + * Increase the number of rows in this cellmap by aNumRows. Put the + * new rows at aRowIndex. If aRowIndex is -1, put them at the end. + */ + bool Grow(nsTableCellMap& aMap, int32_t aNumRows, int32_t aRowIndex = -1); + + void GrowRow(CellDataArray& aRow, int32_t aNumCols); + + /** assign aCellData to the cell at (aRow,aColumn) */ + void SetDataAt(nsTableCellMap& aMap, CellData& aCellData, + int32_t aMapRowIndex, int32_t aColIndex); + + CellData* GetDataAt(int32_t aMapRowIndex, int32_t aColIndex) const; + + int32_t GetNumCellsIn(int32_t aColIndex) const; + + void ExpandWithRows(nsTableCellMap& aMap, + nsTArray<nsTableRowFrame*>& aRowFrames, + int32_t aStartRowIndex, int32_t aRgFirstRowIndex, + TableArea& aDamageArea); + + void ExpandWithCells(nsTableCellMap& aMap, + nsTArray<nsTableCellFrame*>& aCellFrames, + int32_t aRowIndex, int32_t aColIndex, int32_t aRowSpan, + bool aRowSpanIsZero, int32_t aRgFirstRowIndex, + TableArea& aDamageArea); + + void ShrinkWithoutRows(nsTableCellMap& aMap, int32_t aFirstRowIndex, + int32_t aNumRowsToRemove, int32_t aRgFirstRowIndex, + TableArea& aDamageArea); + + void ShrinkWithoutCell(nsTableCellMap& aMap, nsTableCellFrame& aCellFrame, + int32_t aRowIndex, int32_t aColIndex, + int32_t aRgFirstRowIndex, TableArea& aDamageArea); + + /** + * Rebuild due to rows being inserted or deleted with cells spanning + * into or out of the rows. This function can only handle insertion + * or deletion but NOT both. So either aRowsToInsert must be null + * or aNumRowsToRemove must be 0. + * + * // XXXbz are both allowed to happen? That'd be a no-op... + */ + void RebuildConsideringRows(nsTableCellMap& aMap, int32_t aStartRowIndex, + nsTArray<nsTableRowFrame*>* aRowsToInsert, + int32_t aNumRowsToRemove); + + void RebuildConsideringCells(nsTableCellMap& aMap, int32_t aNumOrigCols, + nsTArray<nsTableCellFrame*>* aCellFrames, + int32_t aRowIndex, int32_t aColIndex, + bool aInsert); + + bool CellsSpanOut(nsTArray<nsTableRowFrame*>& aNewRows) const; + + /** If a cell spans out of the area defined by aStartRowIndex, aEndRowIndex + * and aStartColIndex, aEndColIndex the cellmap changes are more severe so + * the corresponding routines needs to be called. This is also necessary if + * cells outside spans into this region. + * @aStartRowIndex - y start index + * @aEndRowIndex - y end index + * @param aStartColIndex - x start index + * @param aEndColIndex - x end index + * @return - true if a cell span crosses the border of the + region + */ + bool CellsSpanInOrOut(int32_t aStartRowIndex, int32_t aEndRowIndex, + int32_t aStartColIndex, int32_t aEndColIndex) const; + + bool CreateEmptyRow(int32_t aRowIndex, int32_t aNumCols); + + int32_t GetRowSpanForNewCell(nsTableCellFrame* aCellFrameToAdd, + int32_t aRowIndex, bool& aIsZeroRowSpan) const; + + // Destroy a CellData struct. This will handle the case of aData + // actually being a BCCellData properly. + void DestroyCellData(CellData* aData); + // Allocate a CellData struct. This will handle needing to create a + // BCCellData properly. + // @param aOrigCell the originating cell to pass to the celldata constructor + CellData* AllocCellData(nsTableCellFrame* aOrigCell); + + /** an array containing, for each row, the CellDatas for the cells + * in that row. It can be larger than mContentRowCount due to row spans + * extending beyond the table */ + // XXXbz once we have auto TArrays, we should probably use them here. + nsTArray<CellDataArray> mRows; + + /** the number of rows in the table (content) which is not indentical to the + * number of rows in the cell map due to row spans extending beyond the end + * of thetable (dead rows) or empty tr tags + */ + int32_t mContentRowCount; + + // the row group that corresponds to this map + nsTableRowGroupFrame* mRowGroupFrame; + + // the next row group cell map + nsCellMap* mNextSibling; + + // Whether this is a BC cellmap or not + bool mIsBC; + + // Prescontext to deallocate and allocate celldata + RefPtr<nsPresContext> mPresContext; +}; + +/** + * A class for iterating the cells in a given column. Must be given a + * non-null nsTableCellMap and a column number valid for that cellmap. + */ +class nsCellMapColumnIterator { + public: + nsCellMapColumnIterator(const nsTableCellMap* aMap, int32_t aCol) + : mMap(aMap), + mCurMap(aMap->mFirstMap), + mCurMapStart(0), + mCurMapRow(0), + mCol(aCol), + mFoundCells(0), + mCurMapContentRowCount(0), + mCurMapRelevantRowCount(0) { + MOZ_ASSERT(aMap, "Must have map"); + MOZ_ASSERT(mCol < aMap->GetColCount(), "Invalid column"); + mOrigCells = aMap->GetNumCellsOriginatingInCol(mCol); + if (mCurMap) { + mCurMapContentRowCount = mCurMap->GetRowCount(); + uint32_t rowArrayLength = mCurMap->mRows.Length(); + mCurMapRelevantRowCount = + std::min(mCurMapContentRowCount, rowArrayLength); + if (mCurMapRelevantRowCount == 0 && mOrigCells > 0) { + // This row group is useless; advance! + AdvanceRowGroup(); + } + } +#ifdef DEBUG + else { + NS_ASSERTION(mOrigCells == 0, "Why no rowgroups?"); + } +#endif + } + + nsTableCellFrame* GetNextFrame(int32_t* aRow, int32_t* aColSpan); + + private: + void AdvanceRowGroup(); + + // Advance the row; aIncrement is considered to be a cell's rowspan, + // so if 0 is passed in we'll advance to the next rowgroup. + void IncrementRow(int32_t aIncrement); + + const nsTableCellMap* mMap; + const nsCellMap* mCurMap; + + // mCurMapStart is the row in the entire nsTableCellMap where + // mCurMap starts. This is used to compute row indices to pass to + // nsTableCellMap::GetDataAt, so must be a _content_ row index. + uint32_t mCurMapStart; + + // In steady-state mCurMapRow is the row in our current nsCellMap + // that we'll use the next time GetNextFrame() is called. Due to + // the way we skip over rowspans, the entry in mCurMapRow and mCol + // is either null, dead, originating, or a colspan. In particular, + // it cannot be a rowspan or overlap entry. + uint32_t mCurMapRow; + const int32_t mCol; + uint32_t mOrigCells; + uint32_t mFoundCells; + + // The number of content rows in mCurMap. This may be bigger than the number + // of "relevant" rows, or it might be smaller. + uint32_t mCurMapContentRowCount; + + // The number of "relevant" rows in mCurMap. That is, the number of rows + // which might have an originating cell in them. Once mCurMapRow reaches + // mCurMapRelevantRowCount, we should move to the next map. + uint32_t mCurMapRelevantRowCount; +}; + +/* ----- inline methods ----- */ +inline int32_t nsTableCellMap::GetColCount() const { return mCols.Length(); } + +inline nsCellMap* nsCellMap::GetNextSibling() const { return mNextSibling; } + +inline void nsCellMap::SetNextSibling(nsCellMap* aSibling) { + mNextSibling = aSibling; +} + +inline nsTableRowGroupFrame* nsCellMap::GetRowGroup() const { + return mRowGroupFrame; +} + +inline int32_t nsCellMap::GetRowCount(bool aConsiderDeadRowSpanRows) const { + int32_t rowCount = + (aConsiderDeadRowSpanRows) ? mRows.Length() : mContentRowCount; + return rowCount; +} + +// nsColInfo + +inline nsColInfo::nsColInfo() : mNumCellsOrig(0), mNumCellsSpan(0) {} + +inline nsColInfo::nsColInfo(int32_t aNumCellsOrig, int32_t aNumCellsSpan) + : mNumCellsOrig(aNumCellsOrig), mNumCellsSpan(aNumCellsSpan) {} + +#endif |