1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
12 #include "nsAlgorithm.h"
15 #include "TableArea.h"
17 #undef DEBUG_TABLE_CELLMAP
19 class nsTableCellFrame
;
20 class nsTableRowFrame
;
21 class nsTableRowGroupFrame
;
25 class nsCellMapColumnIterator
;
28 int32_t mNumCellsOrig
; // number of cells originating in the col
29 int32_t mNumCellsSpan
; // number of cells spanning into the col via colspans
33 nsColInfo(int32_t aNumCellsOrig
, int32_t aNumCellsSpan
);
37 nsTArray
<BCData
> mIEndBorders
;
38 nsTArray
<BCData
> mBEndBorders
;
39 BCData mBEndIEndCorner
;
42 class nsTableCellMap
{
43 typedef mozilla::TableArea TableArea
;
46 nsTableCellMap(nsTableFrame
& aTableFrame
, bool aBorderCollapse
);
49 * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
53 void RemoveGroupCellMap(nsTableRowGroupFrame
* aRowGroup
);
55 void InsertGroupCellMap(nsTableRowGroupFrame
* aNewRowGroup
,
56 nsTableRowGroupFrame
*& aPrevRowGroup
);
59 * Get the nsCellMap for the given row group. If aStartHint is non-null,
60 * will start looking with that cellmap and only fall back to starting at the
61 * beginning of the list if that doesn't find us the right nsCellMap.
62 * Otherwise, just start at the beginning.
64 * aRowGroup must not be null.
66 nsCellMap
* GetMapFor(const nsTableRowGroupFrame
* aRowGroup
,
67 nsCellMap
* aStartHint
) const;
69 /** synchronize the cellmaps with the rowgroups again **/
70 void Synchronize(nsTableFrame
* aTableFrame
);
72 nsTableCellFrame
* GetCellFrame(int32_t aRowIndex
, int32_t aColIndex
,
73 CellData
& aData
, bool aUseRowIfOverlap
) const;
75 /** return the CellData for the cell at (aRowIndex, aColIndex) */
76 CellData
* GetDataAt(int32_t aRowIndex
, int32_t aColIndex
) const;
78 // this function creates a col if needed
79 nsColInfo
* GetColInfoAt(int32_t aColIndex
);
81 /** append the cellFrame at the end of the row at aRowIndex and return the col
84 CellData
* AppendCell(nsTableCellFrame
& aCellFrame
, int32_t aRowIndex
,
85 bool aRebuildIfNecessary
, TableArea
& aDamageArea
);
87 void InsertCells(nsTArray
<nsTableCellFrame
*>& aCellFrames
, int32_t aRowIndex
,
88 int32_t aColIndexBefore
, TableArea
& aDamageArea
);
90 void RemoveCell(nsTableCellFrame
* aCellFrame
, int32_t aRowIndex
,
91 TableArea
& aDamageArea
);
92 /** Remove the previously gathered column information */
94 void InsertRows(nsTableRowGroupFrame
* aRowGroup
,
95 nsTArray
<nsTableRowFrame
*>& aRows
, int32_t aFirstRowIndex
,
96 bool aConsiderSpans
, TableArea
& aDamageArea
);
98 void RemoveRows(int32_t aFirstRowIndex
, int32_t aNumRowsToRemove
,
99 bool aConsiderSpans
, TableArea
& aDamageArea
);
101 int32_t GetNumCellsOriginatingInRow(int32_t aRowIndex
) const;
102 int32_t GetNumCellsOriginatingInCol(int32_t aColIndex
) const;
104 /** indicate whether the row has more than one cell that either originates
105 * or is spanned from the rows above
107 bool HasMoreThanOneCell(int32_t aRowIndex
) const;
109 int32_t GetEffectiveRowSpan(int32_t aRowIndex
, int32_t aColIndex
) const;
110 int32_t GetEffectiveColSpan(int32_t aRowIndex
, int32_t aColIndex
) const;
112 /** return the total number of columns in the table represented by this
114 int32_t GetColCount() const;
116 /** return the actual number of rows in the table represented by this CellMap
118 int32_t GetRowCount() const;
120 nsTableCellFrame
* GetCellInfoAt(int32_t aRowX
, int32_t aColX
,
121 bool* aOriginates
= nullptr,
122 int32_t* aColSpan
= nullptr) const;
125 * Returns the index at the given row and column coordinates.
127 * @see nsITableLayout::GetIndexByRowAndColumn()
129 * @param aRow [in] the row coordinate
130 * @param aColumn [in] the column coordinate
131 * @returns the index for the cell
133 int32_t GetIndexByRowAndColumn(int32_t aRow
, int32_t aColumn
) const;
136 * Retrieves the row and column coordinates for the given index.
138 * @see nsITableLayout::GetRowAndColumnByIndex()
140 * @param aIndex [in] the index for which coordinates are to be retrieved
141 * @param aRow [out] the row coordinate to be returned
142 * @param aColumn [out] the column coordinate to be returned
144 void GetRowAndColumnByIndex(int32_t aIndex
, int32_t* aRow
,
145 int32_t* aColumn
) const;
147 void AddColsAtEnd(uint32_t aNumCols
);
148 void RemoveColsAtEnd();
150 bool RowIsSpannedInto(int32_t aRowIndex
, int32_t aNumEffCols
) const;
151 bool RowHasSpanningCells(int32_t aRowIndex
, int32_t aNumEffCols
) const;
152 void RebuildConsideringCells(nsCellMap
* aCellMap
,
153 nsTArray
<nsTableCellFrame
*>* aCellFrames
,
154 int32_t aRowIndex
, int32_t aColIndex
,
155 bool aInsert
, TableArea
& aDamageArea
);
159 * Rebuild due to rows being inserted or deleted with cells spanning
160 * into or out of the rows. This function can only handle insertion
161 * or deletion but NOT both. So either aRowsToInsert must be null
162 * or aNumRowsToRemove must be 0.
164 * // XXXbz are both allowed to happen? That'd be a no-op...
166 void RebuildConsideringRows(nsCellMap
* aCellMap
, int32_t aStartRowIndex
,
167 nsTArray
<nsTableRowFrame
*>* aRowsToInsert
,
168 int32_t aNumRowsToRemove
, TableArea
& aDamageArea
);
171 void ResetBStartStart(mozilla::LogicalSide aSide
, nsCellMap
& aCellMap
,
172 uint32_t aRowGroupStart
, uint32_t aYPos
,
175 void SetBCBorderEdge(mozilla::LogicalSide aEdge
, nsCellMap
& aCellMap
,
176 uint32_t aCellMapStart
, uint32_t aYPos
, uint32_t aXPos
,
177 uint32_t aLength
, BCBorderOwner aOwner
, nscoord aSize
,
180 void SetBCBorderCorner(mozilla::LogicalCorner aCorner
, nsCellMap
& aCellMap
,
181 uint32_t aCellMapStart
, uint32_t aYPos
, uint32_t aXPos
,
182 mozilla::LogicalSide aOwner
, nscoord aSubSize
,
183 bool aBevel
, bool aIsBottomRight
= false);
185 /** dump a representation of the cell map to stdout for debugging */
187 void Dump(char* aString
= nullptr) const;
191 BCData
* GetIEndMostBorder(int32_t aRowIndex
);
192 BCData
* GetBEndMostBorder(int32_t aColIndex
);
194 friend class nsCellMap
;
195 friend class BCMapCellIterator
;
196 friend class BCPaintBorderIterator
;
197 friend class nsCellMapColumnIterator
;
199 /** Insert a row group cellmap after aPrevMap, if aPrefMap is null insert it
200 * at the beginning, the ordering of the cellmap corresponds to the ordering
201 * of rowgroups once OrderRowGroups has been called
203 void InsertGroupCellMap(nsCellMap
* aPrevMap
, nsCellMap
& aNewMap
);
204 void DeleteIEndBEndBorders();
206 nsTableFrame
& mTableFrame
;
207 AutoTArray
<nsColInfo
, 8> mCols
;
208 nsCellMap
* mFirstMap
;
209 // border collapsing info
213 /** nsCellMap is a support class for nsTablePart.
214 * It maintains an Rows x Columns grid onto which the cells of the table are
215 * mapped. This makes processing of rowspan and colspan attributes much easier.
216 * Each cell is represented by a CellData object.
219 * @see nsTableFrame::AddCellToMap
220 * @see nsTableFrame::GrowCellMap
221 * @see nsTableFrame::BuildCellIntoMap
223 * mRows is an array of rows. Each row is an array of cells. a cell
227 typedef mozilla::TableArea TableArea
;
231 * @param aRowGroupFrame the row group frame this is a cellmap for
232 * @param aIsBC whether the table is doing border-collapse
234 nsCellMap(nsTableRowGroupFrame
* aRowGroupFrame
, bool aIsBC
);
237 * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
242 static void Shutdown();
244 nsCellMap
* GetNextSibling() const;
245 void SetNextSibling(nsCellMap
* aSibling
);
247 nsTableRowGroupFrame
* GetRowGroup() const;
249 nsTableCellFrame
* GetCellFrame(int32_t aRowIndex
, int32_t aColIndex
,
251 bool aUseRowSpanIfOverlap
) const;
254 * Returns highest cell index within the cell map.
256 * @param aColCount [in] the number of columns in the table
258 int32_t GetHighestIndex(int32_t aColCount
);
261 * Returns the index of the given row and column coordinates.
263 * @see nsITableLayout::GetIndexByRowAndColumn()
265 * @param aColCount [in] the number of columns in the table
266 * @param aRow [in] the row coordinate
267 * @param aColumn [in] the column coordinate
269 int32_t GetIndexByRowAndColumn(int32_t aColCount
, int32_t aRow
,
270 int32_t aColumn
) const;
273 * Get the row and column coordinates at the given index.
275 * @see nsITableLayout::GetRowAndColumnByIndex()
277 * @param aColCount [in] the number of columns in the table
278 * @param aIndex [in] the index for which coordinates are to be retrieved
279 * @param aRow [out] the row coordinate to be returned
280 * @param aColumn [out] the column coordinate to be returned
282 void GetRowAndColumnByIndex(int32_t aColCount
, int32_t aIndex
, int32_t* aRow
,
283 int32_t* aColumn
) const;
285 /** append the cellFrame at an empty or dead cell or finally at the end of
286 * the row at aRowIndex and return a pointer to the celldata entry in the
289 * @param aMap - reference to the table cell map
290 * @param aCellFrame - a pointer to the cellframe which will be
291 * appended to the row
292 * @param aRowIndex - to this row the celldata entry will be added
293 * @param aRebuildIfNecessay - if a cell spans into a row below it might be
294 * necesserary to rebuild the cellmap as this
295 * rowspan might overlap another cell.
296 * @param aDamageArea - area in cellmap coordinates which have been
298 * @param aColToBeginSearch - if not null contains the column number where
299 * the search for a empty or dead cell in the
301 * @return - a pointer to the celldata entry inserted into
304 CellData
* AppendCell(nsTableCellMap
& aMap
, nsTableCellFrame
* aCellFrame
,
305 int32_t aRowIndex
, bool aRebuildIfNecessary
,
306 int32_t aRgFirstRowIndex
, TableArea
& aDamageArea
,
307 int32_t* aBeginSearchAtCol
= nullptr);
309 void InsertCells(nsTableCellMap
& aMap
,
310 nsTArray
<nsTableCellFrame
*>& aCellFrames
, int32_t aRowIndex
,
311 int32_t aColIndexBefore
, int32_t aRgFirstRowIndex
,
312 TableArea
& aDamageArea
);
314 void RemoveCell(nsTableCellMap
& aMap
, nsTableCellFrame
* aCellFrame
,
315 int32_t aRowIndex
, int32_t aRgFirstRowIndex
,
316 TableArea
& aDamageArea
);
318 void InsertRows(nsTableCellMap
& aMap
, nsTArray
<nsTableRowFrame
*>& aRows
,
319 int32_t aFirstRowIndex
, bool aConsiderSpans
,
320 int32_t aRgFirstRowIndex
, TableArea
& aDamageArea
);
322 void RemoveRows(nsTableCellMap
& aMap
, int32_t aFirstRowIndex
,
323 int32_t aNumRowsToRemove
, bool aConsiderSpans
,
324 int32_t aRgFirstRowIndex
, TableArea
& aDamageArea
);
326 int32_t GetNumCellsOriginatingInRow(int32_t aRowIndex
) const;
327 int32_t GetNumCellsOriginatingInCol(int32_t aColIndex
) const;
329 /** return the number of rows in the table represented by this CellMap */
330 int32_t GetRowCount(bool aConsiderDeadRowSpanRows
= false) const;
332 nsTableCellFrame
* GetCellInfoAt(const nsTableCellMap
& aMap
, int32_t aRowX
,
333 int32_t aColX
, bool* aOriginates
= nullptr,
334 int32_t* aColSpan
= nullptr) const;
336 bool RowIsSpannedInto(int32_t aRowIndex
, int32_t aNumEffCols
) const;
338 bool RowHasSpanningCells(int32_t aRowIndex
, int32_t aNumEffCols
) const;
340 /** indicate whether the row has more than one cell that either originates
341 * or is spanned from the rows above
343 bool HasMoreThanOneCell(int32_t aRowIndex
) const;
345 /* Get the rowspan for a cell starting at aRowIndex and aColIndex.
346 * If aGetEffective is true the size will not exceed the last content based
347 * row. Cells can have a specified rowspan that extends below the last
348 * content based row. This is legitimate considering incr. reflow where the
349 * content rows will arive later.
351 int32_t GetRowSpan(int32_t aRowIndex
, int32_t aColIndex
,
352 bool aGetEffective
) const;
354 int32_t GetEffectiveColSpan(const nsTableCellMap
& aMap
, int32_t aRowIndex
,
355 int32_t aColIndex
) const;
357 typedef nsTArray
<CellData
*> CellDataArray
;
359 /** dump a representation of the cell map to stdout for debugging */
361 void Dump(bool aIsBorderCollapse
) const;
365 friend class nsTableCellMap
;
366 friend class BCMapCellIterator
;
367 friend class BCPaintBorderIterator
;
368 friend class nsTableFrame
;
369 friend class nsCellMapColumnIterator
;
372 * Increase the number of rows in this cellmap by aNumRows. Put the
373 * new rows at aRowIndex. If aRowIndex is -1, put them at the end.
375 bool Grow(nsTableCellMap
& aMap
, int32_t aNumRows
, int32_t aRowIndex
= -1);
377 void GrowRow(CellDataArray
& aRow
, int32_t aNumCols
);
379 /** assign aCellData to the cell at (aRow,aColumn) */
380 void SetDataAt(nsTableCellMap
& aMap
, CellData
& aCellData
,
381 int32_t aMapRowIndex
, int32_t aColIndex
);
383 CellData
* GetDataAt(int32_t aMapRowIndex
, int32_t aColIndex
) const;
385 int32_t GetNumCellsIn(int32_t aColIndex
) const;
387 void ExpandWithRows(nsTableCellMap
& aMap
,
388 nsTArray
<nsTableRowFrame
*>& aRowFrames
,
389 int32_t aStartRowIndex
, int32_t aRgFirstRowIndex
,
390 TableArea
& aDamageArea
);
392 void ExpandWithCells(nsTableCellMap
& aMap
,
393 nsTArray
<nsTableCellFrame
*>& aCellFrames
,
394 int32_t aRowIndex
, int32_t aColIndex
, int32_t aRowSpan
,
395 bool aRowSpanIsZero
, int32_t aRgFirstRowIndex
,
396 TableArea
& aDamageArea
);
398 void ShrinkWithoutRows(nsTableCellMap
& aMap
, int32_t aFirstRowIndex
,
399 int32_t aNumRowsToRemove
, int32_t aRgFirstRowIndex
,
400 TableArea
& aDamageArea
);
402 void ShrinkWithoutCell(nsTableCellMap
& aMap
, nsTableCellFrame
& aCellFrame
,
403 int32_t aRowIndex
, int32_t aColIndex
,
404 int32_t aRgFirstRowIndex
, TableArea
& aDamageArea
);
407 * Rebuild due to rows being inserted or deleted with cells spanning
408 * into or out of the rows. This function can only handle insertion
409 * or deletion but NOT both. So either aRowsToInsert must be null
410 * or aNumRowsToRemove must be 0.
412 * // XXXbz are both allowed to happen? That'd be a no-op...
414 void RebuildConsideringRows(nsTableCellMap
& aMap
, int32_t aStartRowIndex
,
415 nsTArray
<nsTableRowFrame
*>* aRowsToInsert
,
416 int32_t aNumRowsToRemove
);
418 void RebuildConsideringCells(nsTableCellMap
& aMap
, int32_t aNumOrigCols
,
419 nsTArray
<nsTableCellFrame
*>* aCellFrames
,
420 int32_t aRowIndex
, int32_t aColIndex
,
423 bool CellsSpanOut(nsTArray
<nsTableRowFrame
*>& aNewRows
) const;
425 /** If a cell spans out of the area defined by aStartRowIndex, aEndRowIndex
426 * and aStartColIndex, aEndColIndex the cellmap changes are more severe so
427 * the corresponding routines needs to be called. This is also necessary if
428 * cells outside spans into this region.
429 * @aStartRowIndex - y start index
430 * @aEndRowIndex - y end index
431 * @param aStartColIndex - x start index
432 * @param aEndColIndex - x end index
433 * @return - true if a cell span crosses the border of the
436 bool CellsSpanInOrOut(int32_t aStartRowIndex
, int32_t aEndRowIndex
,
437 int32_t aStartColIndex
, int32_t aEndColIndex
) const;
439 bool CreateEmptyRow(int32_t aRowIndex
, int32_t aNumCols
);
441 int32_t GetRowSpanForNewCell(nsTableCellFrame
* aCellFrameToAdd
,
442 int32_t aRowIndex
, bool& aIsZeroRowSpan
) const;
444 // Destroy a CellData struct. This will handle the case of aData
445 // actually being a BCCellData properly.
446 void DestroyCellData(CellData
* aData
);
447 // Allocate a CellData struct. This will handle needing to create a
448 // BCCellData properly.
449 // @param aOrigCell the originating cell to pass to the celldata constructor
450 CellData
* AllocCellData(nsTableCellFrame
* aOrigCell
);
452 /** an array containing, for each row, the CellDatas for the cells
453 * in that row. It can be larger than mContentRowCount due to row spans
454 * extending beyond the table */
455 // XXXbz once we have auto TArrays, we should probably use them here.
456 nsTArray
<CellDataArray
> mRows
;
458 /** the number of rows in the table (content) which is not indentical to the
459 * number of rows in the cell map due to row spans extending beyond the end
460 * of thetable (dead rows) or empty tr tags
462 int32_t mContentRowCount
;
464 // the row group that corresponds to this map
465 nsTableRowGroupFrame
* mRowGroupFrame
;
467 // the next row group cell map
468 nsCellMap
* mNextSibling
;
470 // Whether this is a BC cellmap or not
473 // Prescontext to deallocate and allocate celldata
474 RefPtr
<nsPresContext
> mPresContext
;
478 * A class for iterating the cells in a given column. Must be given a
479 * non-null nsTableCellMap and a column number valid for that cellmap.
481 class nsCellMapColumnIterator
{
483 nsCellMapColumnIterator(const nsTableCellMap
* aMap
, int32_t aCol
)
485 mCurMap(aMap
->mFirstMap
),
490 mCurMapContentRowCount(0),
491 mCurMapRelevantRowCount(0) {
492 MOZ_ASSERT(aMap
, "Must have map");
493 MOZ_ASSERT(mCol
< aMap
->GetColCount(), "Invalid column");
494 mOrigCells
= aMap
->GetNumCellsOriginatingInCol(mCol
);
496 mCurMapContentRowCount
= mCurMap
->GetRowCount();
497 uint32_t rowArrayLength
= mCurMap
->mRows
.Length();
498 mCurMapRelevantRowCount
=
499 std::min(mCurMapContentRowCount
, rowArrayLength
);
500 if (mCurMapRelevantRowCount
== 0 && mOrigCells
> 0) {
501 // This row group is useless; advance!
507 NS_ASSERTION(mOrigCells
== 0, "Why no rowgroups?");
512 nsTableCellFrame
* GetNextFrame(int32_t* aRow
, int32_t* aColSpan
);
515 void AdvanceRowGroup();
517 // Advance the row; aIncrement is considered to be a cell's rowspan,
518 // so if 0 is passed in we'll advance to the next rowgroup.
519 void IncrementRow(int32_t aIncrement
);
521 const nsTableCellMap
* mMap
;
522 const nsCellMap
* mCurMap
;
524 // mCurMapStart is the row in the entire nsTableCellMap where
525 // mCurMap starts. This is used to compute row indices to pass to
526 // nsTableCellMap::GetDataAt, so must be a _content_ row index.
527 uint32_t mCurMapStart
;
529 // In steady-state mCurMapRow is the row in our current nsCellMap
530 // that we'll use the next time GetNextFrame() is called. Due to
531 // the way we skip over rowspans, the entry in mCurMapRow and mCol
532 // is either null, dead, originating, or a colspan. In particular,
533 // it cannot be a rowspan or overlap entry.
537 uint32_t mFoundCells
;
539 // The number of content rows in mCurMap. This may be bigger than the number
540 // of "relevant" rows, or it might be smaller.
541 uint32_t mCurMapContentRowCount
;
543 // The number of "relevant" rows in mCurMap. That is, the number of rows
544 // which might have an originating cell in them. Once mCurMapRow reaches
545 // mCurMapRelevantRowCount, we should move to the next map.
546 uint32_t mCurMapRelevantRowCount
;
549 /* ----- inline methods ----- */
550 inline int32_t nsTableCellMap::GetColCount() const { return mCols
.Length(); }
552 inline nsCellMap
* nsCellMap::GetNextSibling() const { return mNextSibling
; }
554 inline void nsCellMap::SetNextSibling(nsCellMap
* aSibling
) {
555 mNextSibling
= aSibling
;
558 inline nsTableRowGroupFrame
* nsCellMap::GetRowGroup() const {
559 return mRowGroupFrame
;
562 inline int32_t nsCellMap::GetRowCount(bool aConsiderDeadRowSpanRows
) const {
564 (aConsiderDeadRowSpanRows
) ? mRows
.Length() : mContentRowCount
;
570 inline nsColInfo::nsColInfo() : mNumCellsOrig(0), mNumCellsSpan(0) {}
572 inline nsColInfo::nsColInfo(int32_t aNumCellsOrig
, int32_t aNumCellsSpan
)
573 : mNumCellsOrig(aNumCellsOrig
), mNumCellsSpan(aNumCellsSpan
) {}