bug 523370 - fixing bogus OOM with empty double free lists. r=dmandelin
[mozilla-central.git] / layout / tables / nsCellMap.cpp
blob43442fea69a09fabca88256d336c74a8a7ab9bc0
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsTArray.h"
39 #include "nsCellMap.h"
40 #include "nsTableFrame.h"
41 #include "nsTableCellFrame.h"
42 #include "nsTableRowGroupFrame.h"
44 // Empty static array used for SafeElementAt() calls on mRows.
45 static nsCellMap::CellDataArray * sEmptyRow;
47 // CellData
49 CellData::CellData(nsTableCellFrame* aOrigCell)
51 MOZ_COUNT_CTOR(CellData);
52 mOrigCell = aOrigCell;
55 CellData::~CellData()
57 MOZ_COUNT_DTOR(CellData);
60 BCCellData::BCCellData(nsTableCellFrame* aOrigCell)
61 :CellData(aOrigCell)
63 MOZ_COUNT_CTOR(BCCellData);
66 BCCellData::~BCCellData()
68 MOZ_COUNT_DTOR(BCCellData);
71 // nsTableCellMap
73 nsTableCellMap::nsTableCellMap(nsTableFrame& aTableFrame,
74 PRBool aBorderCollapse)
75 :mTableFrame(aTableFrame), mFirstMap(nsnull), mBCInfo(nsnull)
77 MOZ_COUNT_CTOR(nsTableCellMap);
79 nsTableFrame::RowGroupArray orderedRowGroups;
80 aTableFrame.OrderRowGroups(orderedRowGroups);
82 nsTableRowGroupFrame* prior = nsnull;
83 for (PRUint32 rgX = 0; rgX < orderedRowGroups.Length(); rgX++) {
84 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX];
85 InsertGroupCellMap(*rgFrame, prior);
86 prior = rgFrame;
88 if (aBorderCollapse) {
89 mBCInfo = new BCInfo();
93 nsTableCellMap::~nsTableCellMap()
95 MOZ_COUNT_DTOR(nsTableCellMap);
97 nsCellMap* cellMap = mFirstMap;
98 while (cellMap) {
99 nsCellMap* next = cellMap->GetNextSibling();
100 delete cellMap;
101 cellMap = next;
104 if (mBCInfo) {
105 DeleteRightBottomBorders();
106 delete mBCInfo;
110 // Get the bcData holding the border segments of the right edge of the table
111 BCData*
112 nsTableCellMap::GetRightMostBorder(PRInt32 aRowIndex)
114 if (!mBCInfo) ABORT1(nsnull);
116 PRInt32 numRows = mBCInfo->mRightBorders.Length();
117 if (aRowIndex < numRows) {
118 return &mBCInfo->mRightBorders.ElementAt(aRowIndex);
121 if (!mBCInfo->mRightBorders.SetLength(aRowIndex+1))
122 ABORT1(nsnull);
123 return &mBCInfo->mRightBorders.ElementAt(aRowIndex);
126 // Get the bcData holding the border segments of the bottom edge of the table
127 BCData*
128 nsTableCellMap::GetBottomMostBorder(PRInt32 aColIndex)
130 if (!mBCInfo) ABORT1(nsnull);
132 PRInt32 numCols = mBCInfo->mBottomBorders.Length();
133 if (aColIndex < numCols) {
134 return &mBCInfo->mBottomBorders.ElementAt(aColIndex);
137 if (!mBCInfo->mBottomBorders.SetLength(aColIndex+1))
138 ABORT1(nsnull);
139 return &mBCInfo->mBottomBorders.ElementAt(aColIndex);
142 // delete the borders corresponding to the right and bottom edges of the table
143 void
144 nsTableCellMap::DeleteRightBottomBorders()
146 if (mBCInfo) {
147 mBCInfo->mBottomBorders.Clear();
148 mBCInfo->mRightBorders.Clear();
152 void
153 nsTableCellMap::InsertGroupCellMap(nsCellMap* aPrevMap,
154 nsCellMap& aNewMap)
156 nsCellMap* next;
157 if (aPrevMap) {
158 next = aPrevMap->GetNextSibling();
159 aPrevMap->SetNextSibling(&aNewMap);
161 else {
162 next = mFirstMap;
163 mFirstMap = &aNewMap;
165 aNewMap.SetNextSibling(next);
168 void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame& aNewGroup,
169 nsTableRowGroupFrame*& aPrevGroup)
171 nsCellMap* newMap = new nsCellMap(aNewGroup, mBCInfo != nsnull);
172 if (newMap) {
173 nsCellMap* prevMap = nsnull;
174 nsCellMap* lastMap = mFirstMap;
175 if (aPrevGroup) {
176 nsCellMap* map = mFirstMap;
177 while (map) {
178 lastMap = map;
179 if (map->GetRowGroup() == aPrevGroup) {
180 prevMap = map;
181 break;
183 map = map->GetNextSibling();
186 if (!prevMap) {
187 if (aPrevGroup) {
188 prevMap = lastMap;
189 aPrevGroup = (prevMap) ? prevMap->GetRowGroup() : nsnull;
191 else {
192 aPrevGroup = nsnull;
195 InsertGroupCellMap(prevMap, *newMap);
199 void nsTableCellMap::RemoveGroupCellMap(nsTableRowGroupFrame* aGroup)
201 nsCellMap* map = mFirstMap;
202 nsCellMap* prior = nsnull;
203 while (map) {
204 if (map->GetRowGroup() == aGroup) {
205 nsCellMap* next = map->GetNextSibling();
206 if (mFirstMap == map) {
207 mFirstMap = next;
209 else {
210 prior->SetNextSibling(next);
212 delete map;
213 break;
215 prior = map;
216 map = map->GetNextSibling();
220 static nsCellMap*
221 FindMapFor(const nsTableRowGroupFrame* aRowGroup,
222 nsCellMap* aStart,
223 const nsCellMap* aEnd)
225 for (nsCellMap* map = aStart; map != aEnd; map = map->GetNextSibling()) {
226 if (aRowGroup == map->GetRowGroup()) {
227 return map;
231 return nsnull;
234 nsCellMap*
235 nsTableCellMap::GetMapFor(const nsTableRowGroupFrame* aRowGroup,
236 nsCellMap* aStartHint) const
238 NS_PRECONDITION(aRowGroup, "Must have a rowgroup");
239 NS_ASSERTION(!aRowGroup->GetPrevInFlow(), "GetMapFor called with continuation");
240 if (aStartHint) {
241 nsCellMap* map = FindMapFor(aRowGroup, aStartHint, nsnull);
242 if (map) {
243 return map;
247 nsCellMap* map = FindMapFor(aRowGroup, mFirstMap, aStartHint);
248 if (map) {
249 return map;
252 // if aRowGroup is a repeated header or footer find the header or footer it was repeated from
253 if (aRowGroup->IsRepeatable()) {
254 nsTableFrame* fifTable = static_cast<nsTableFrame*>(mTableFrame.GetFirstInFlow());
256 const nsStyleDisplay* display = aRowGroup->GetStyleDisplay();
257 nsTableRowGroupFrame* rgOrig =
258 (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay) ?
259 fifTable->GetTHead() : fifTable->GetTFoot();
260 // find the row group cell map using the original header/footer
261 if (rgOrig && rgOrig != aRowGroup) {
262 return GetMapFor(rgOrig, aStartHint);
266 return nsnull;
269 void
270 nsTableCellMap::Synchronize(nsTableFrame* aTableFrame)
272 nsTableFrame::RowGroupArray orderedRowGroups;
273 nsAutoTPtrArray<nsCellMap, 8> maps;
275 aTableFrame->OrderRowGroups(orderedRowGroups);
276 if (!orderedRowGroups.Length()) {
277 return;
280 // XXXbz this fails if orderedRowGroups is missing some row groups
281 // (due to OOM when appending to the array, e.g. -- we leak maps in
282 // that case).
284 // Scope |map| outside the loop so we can use it as a hint.
285 nsCellMap* map = nsnull;
286 for (PRUint32 rgX = 0; rgX < orderedRowGroups.Length(); rgX++) {
287 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX];
288 map = GetMapFor((nsTableRowGroupFrame*)rgFrame->GetFirstInFlow(), map);
289 if (map) {
290 if (!maps.AppendElement(map)) {
291 delete map;
292 map = nsnull;
293 NS_WARNING("Could not AppendElement");
294 break;
299 PRInt32 mapIndex = maps.Length() - 1; // Might end up -1
300 nsCellMap* nextMap = maps.ElementAt(mapIndex);
301 nextMap->SetNextSibling(nsnull);
302 for (mapIndex-- ; mapIndex >= 0; mapIndex--) {
303 nsCellMap* map = maps.ElementAt(mapIndex);
304 map->SetNextSibling(nextMap);
305 nextMap = map;
307 mFirstMap = nextMap;
310 PRBool
311 nsTableCellMap::HasMoreThanOneCell(PRInt32 aRowIndex) const
313 PRInt32 rowIndex = aRowIndex;
314 nsCellMap* map = mFirstMap;
315 while (map) {
316 if (map->GetRowCount() > rowIndex) {
317 return map->HasMoreThanOneCell(rowIndex);
319 rowIndex -= map->GetRowCount();
320 map = map->GetNextSibling();
322 return PR_FALSE;
325 PRInt32
326 nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const
328 PRInt32 rowIndex = aRowIndex;
329 nsCellMap* map = mFirstMap;
330 while (map) {
331 if (map->GetRowCount() > rowIndex) {
332 return map->GetNumCellsOriginatingInRow(rowIndex);
334 rowIndex -= map->GetRowCount();
335 map = map->GetNextSibling();
337 return 0;
339 PRInt32
340 nsTableCellMap::GetEffectiveRowSpan(PRInt32 aRowIndex,
341 PRInt32 aColIndex) const
343 PRInt32 rowIndex = aRowIndex;
344 nsCellMap* map = mFirstMap;
345 while (map) {
346 if (map->GetRowCount() > rowIndex) {
347 return map->GetRowSpan(rowIndex, aColIndex, PR_TRUE);
349 rowIndex -= map->GetRowCount();
350 map = map->GetNextSibling();
352 NS_NOTREACHED("Bogus row index?");
353 return 0;
356 PRInt32
357 nsTableCellMap::GetEffectiveColSpan(PRInt32 aRowIndex,
358 PRInt32 aColIndex) const
360 PRInt32 rowIndex = aRowIndex;
361 nsCellMap* map = mFirstMap;
362 while (map) {
363 if (map->GetRowCount() > rowIndex) {
364 PRBool zeroColSpan;
365 return map->GetEffectiveColSpan(*this, rowIndex, aColIndex, zeroColSpan);
367 rowIndex -= map->GetRowCount();
368 map = map->GetNextSibling();
370 NS_NOTREACHED("Bogus row index?");
371 return 0;
374 nsTableCellFrame*
375 nsTableCellMap::GetCellFrame(PRInt32 aRowIndex,
376 PRInt32 aColIndex,
377 CellData& aData,
378 PRBool aUseRowIfOverlap) const
380 PRInt32 rowIndex = aRowIndex;
381 nsCellMap* map = mFirstMap;
382 while (map) {
383 if (map->GetRowCount() > rowIndex) {
384 return map->GetCellFrame(rowIndex, aColIndex, aData, aUseRowIfOverlap);
386 rowIndex -= map->GetRowCount();
387 map = map->GetNextSibling();
389 return nsnull;
392 nsColInfo*
393 nsTableCellMap::GetColInfoAt(PRInt32 aColIndex)
395 PRInt32 numColsToAdd = aColIndex + 1 - mCols.Length();
396 if (numColsToAdd > 0) {
397 AddColsAtEnd(numColsToAdd); // XXX this could fail to add cols in theory
399 return &mCols.ElementAt(aColIndex);
402 PRInt32
403 nsTableCellMap::GetRowCount() const
405 PRInt32 numRows = 0;
406 nsCellMap* map = mFirstMap;
407 while (map) {
408 numRows += map->GetRowCount();
409 map = map->GetNextSibling();
411 return numRows;
414 CellData*
415 nsTableCellMap::GetDataAt(PRInt32 aRowIndex,
416 PRInt32 aColIndex) const
418 PRInt32 rowIndex = aRowIndex;
419 nsCellMap* map = mFirstMap;
420 while (map) {
421 if (map->GetRowCount() > rowIndex) {
422 return map->GetDataAt(rowIndex, aColIndex);
424 rowIndex -= map->GetRowCount();
425 map = map->GetNextSibling();
427 return nsnull;
430 void
431 nsTableCellMap::AddColsAtEnd(PRUint32 aNumCols)
433 if (!mCols.AppendElements(aNumCols)) {
434 NS_WARNING("Could not AppendElement");
436 if (mBCInfo) {
437 if (!mBCInfo->mBottomBorders.AppendElements(aNumCols)) {
438 NS_WARNING("Could not AppendElement");
443 void
444 nsTableCellMap::RemoveColsAtEnd()
446 // Remove the cols at the end which don't have originating cells or cells spanning
447 // into them. Only do this if the col was created as eColAnonymousCell
448 PRInt32 numCols = GetColCount();
449 PRInt32 lastGoodColIndex = mTableFrame.GetIndexOfLastRealCol();
450 for (PRInt32 colX = numCols - 1; (colX >= 0) && (colX > lastGoodColIndex); colX--) {
451 nsColInfo& colInfo = mCols.ElementAt(colX);
452 if ((colInfo.mNumCellsOrig <= 0) && (colInfo.mNumCellsSpan <= 0)) {
453 mCols.RemoveElementAt(colX);
455 if (mBCInfo) {
456 PRInt32 count = mBCInfo->mBottomBorders.Length();
457 if (colX < count) {
458 mBCInfo->mBottomBorders.RemoveElementAt(colX);
462 else break; // only remove until we encounter the 1st valid one
466 void
467 nsTableCellMap::ClearCols()
469 mCols.Clear();
470 if (mBCInfo)
471 mBCInfo->mBottomBorders.Clear();
473 void
474 nsTableCellMap::InsertRows(nsTableRowGroupFrame& aParent,
475 nsTArray<nsTableRowFrame*>& aRows,
476 PRInt32 aFirstRowIndex,
477 PRBool aConsiderSpans,
478 nsRect& aDamageArea)
480 PRInt32 numNewRows = aRows.Length();
481 if ((numNewRows <= 0) || (aFirstRowIndex < 0)) ABORT0();
483 PRInt32 rowIndex = aFirstRowIndex;
484 nsCellMap* cellMap = mFirstMap;
485 while (cellMap) {
486 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
487 if (rg == &aParent) {
488 cellMap->InsertRows(*this, aRows, rowIndex, aConsiderSpans, aDamageArea);
489 aDamageArea.y = NS_MIN(aFirstRowIndex, aDamageArea.y);
490 aDamageArea.height = NS_MAX(0, GetRowCount() - aDamageArea.y);
491 #ifdef DEBUG_TABLE_CELLMAP
492 Dump("after InsertRows");
493 #endif
494 if (mBCInfo) {
495 PRInt32 count = mBCInfo->mRightBorders.Length();
496 if (aFirstRowIndex < count) {
497 for (PRInt32 rowX = aFirstRowIndex; rowX < aFirstRowIndex + numNewRows; rowX++) {
498 if (!mBCInfo->mRightBorders.InsertElementAt(rowX))
499 ABORT0();
502 else {
503 GetRightMostBorder(aFirstRowIndex); // this will create missing entries
504 for (PRInt32 rowX = aFirstRowIndex + 1; rowX < aFirstRowIndex + numNewRows; rowX++) {
505 if (!mBCInfo->mRightBorders.AppendElement())
506 ABORT0();
510 return;
512 rowIndex -= cellMap->GetRowCount();
513 cellMap = cellMap->GetNextSibling();
516 NS_ERROR("Attempt to insert row into wrong map.");
519 void
520 nsTableCellMap::RemoveRows(PRInt32 aFirstRowIndex,
521 PRInt32 aNumRowsToRemove,
522 PRBool aConsiderSpans,
523 nsRect& aDamageArea)
525 PRInt32 rowIndex = aFirstRowIndex;
526 nsCellMap* cellMap = mFirstMap;
527 while (cellMap) {
528 if (cellMap->GetRowCount() > rowIndex) {
529 cellMap->RemoveRows(*this, rowIndex, aNumRowsToRemove, aConsiderSpans, aDamageArea);
530 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
531 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
532 aDamageArea.height = NS_MAX(0, GetRowCount() - aFirstRowIndex);
533 if (mBCInfo) {
534 for (PRInt32 rowX = aFirstRowIndex + aNumRowsToRemove - 1; rowX >= aFirstRowIndex; rowX--) {
535 if (PRUint32(rowX) < mBCInfo->mRightBorders.Length()) {
536 mBCInfo->mRightBorders.RemoveElementAt(rowX);
540 break;
542 rowIndex -= cellMap->GetRowCount();
543 cellMap = cellMap->GetNextSibling();
545 #ifdef DEBUG_TABLE_CELLMAP
546 Dump("after RemoveRows");
547 #endif
552 CellData*
553 nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame,
554 PRInt32 aRowIndex,
555 PRBool aRebuildIfNecessary,
556 nsRect& aDamageArea)
558 NS_ASSERTION(&aCellFrame == aCellFrame.GetFirstInFlow(), "invalid call on continuing frame");
559 nsIFrame* rgFrame = aCellFrame.GetParent(); // get the row
560 if (!rgFrame) return 0;
561 rgFrame = rgFrame->GetParent(); // get the row group
562 if (!rgFrame) return 0;
564 CellData* result = nsnull;
565 PRInt32 rowIndex = aRowIndex;
566 nsCellMap* cellMap = mFirstMap;
567 while (cellMap) {
568 if (cellMap->GetRowGroup() == rgFrame) {
569 result = cellMap->AppendCell(*this, &aCellFrame, rowIndex, aRebuildIfNecessary, aDamageArea);
570 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
571 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
572 break;
574 rowIndex -= cellMap->GetRowCount();
575 cellMap = cellMap->GetNextSibling();
577 #ifdef DEBUG_TABLE_CELLMAP
578 Dump("after AppendCell");
579 #endif
580 return result;
584 void
585 nsTableCellMap::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
586 PRInt32 aRowIndex,
587 PRInt32 aColIndexBefore,
588 nsRect& aDamageArea)
590 PRInt32 rowIndex = aRowIndex;
591 nsCellMap* cellMap = mFirstMap;
592 while (cellMap) {
593 if (cellMap->GetRowCount() > rowIndex) {
594 cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore, aDamageArea);
595 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
596 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
597 aDamageArea.width = NS_MAX(0, GetColCount() - aColIndexBefore - 1);
598 break;
600 rowIndex -= cellMap->GetRowCount();
601 cellMap = cellMap->GetNextSibling();
603 #ifdef DEBUG_TABLE_CELLMAP
604 Dump("after InsertCells");
605 #endif
609 void
610 nsTableCellMap::RemoveCell(nsTableCellFrame* aCellFrame,
611 PRInt32 aRowIndex,
612 nsRect& aDamageArea)
614 if (!aCellFrame) ABORT0();
615 NS_ASSERTION(aCellFrame == (nsTableCellFrame *)aCellFrame->GetFirstInFlow(),
616 "invalid call on continuing frame");
617 PRInt32 rowIndex = aRowIndex;
618 nsCellMap* cellMap = mFirstMap;
619 while (cellMap) {
620 if (cellMap->GetRowCount() > rowIndex) {
621 cellMap->RemoveCell(*this, aCellFrame, rowIndex, aDamageArea);
622 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
623 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
624 PRInt32 colIndex;
625 aCellFrame->GetColIndex(colIndex);
626 aDamageArea.width = NS_MAX(0, GetColCount() - colIndex - 1);
627 #ifdef DEBUG_TABLE_CELLMAP
628 Dump("after RemoveCell");
629 #endif
630 return;
632 rowIndex -= cellMap->GetRowCount();
633 cellMap = cellMap->GetNextSibling();
635 // if we reach this point - the cell did not get removed, the caller of this routine
636 // will delete the cell and the cellmap will probably hold a reference to
637 // the deleted cell which will cause a subsequent crash when this cell is
638 // referenced later
639 NS_ERROR("nsTableCellMap::RemoveCell - could not remove cell");
642 void
643 SetDamageArea(PRInt32 aXOrigin,
644 PRInt32 aYOrigin,
645 PRInt32 aWidth,
646 PRInt32 aHeight,
647 nsRect& aDamageArea)
649 aDamageArea.x = aXOrigin;
650 aDamageArea.y = aYOrigin;
651 aDamageArea.width = NS_MAX(1, aWidth);
652 aDamageArea.height = NS_MAX(1, aHeight);
655 void
656 nsTableCellMap::RebuildConsideringCells(nsCellMap* aCellMap,
657 nsTArray<nsTableCellFrame*>* aCellFrames,
658 PRInt32 aRowIndex,
659 PRInt32 aColIndex,
660 PRBool aInsert,
661 nsRect& aDamageArea)
663 PRInt32 numOrigCols = GetColCount();
664 ClearCols();
665 nsCellMap* cellMap = mFirstMap;
666 PRInt32 rowCount = 0;
667 while (cellMap) {
668 if (cellMap == aCellMap) {
669 cellMap->RebuildConsideringCells(*this, numOrigCols, aCellFrames, aRowIndex, aColIndex, aInsert, aDamageArea);
672 else {
673 cellMap->RebuildConsideringCells(*this, numOrigCols, nsnull, -1, 0, PR_FALSE, aDamageArea);
675 rowCount += cellMap->GetRowCount();
676 cellMap = cellMap->GetNextSibling();
678 SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea);
681 void
682 nsTableCellMap::RebuildConsideringRows(nsCellMap* aCellMap,
683 PRInt32 aStartRowIndex,
684 nsTArray<nsTableRowFrame*>* aRowsToInsert,
685 PRInt32 aNumRowsToRemove,
686 nsRect& aDamageArea)
688 NS_PRECONDITION(!aRowsToInsert || aNumRowsToRemove == 0,
689 "Can't handle both removing and inserting rows at once");
691 PRInt32 numOrigCols = GetColCount();
692 ClearCols();
693 nsCellMap* cellMap = mFirstMap;
694 PRInt32 rowCount = 0;
695 while (cellMap) {
696 if (cellMap == aCellMap) {
697 cellMap->RebuildConsideringRows(*this, aStartRowIndex, aRowsToInsert, aNumRowsToRemove, aDamageArea);
699 else {
700 cellMap->RebuildConsideringCells(*this, numOrigCols, nsnull, -1, 0, PR_FALSE, aDamageArea);
702 rowCount += cellMap->GetRowCount();
703 cellMap = cellMap->GetNextSibling();
705 SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea);
708 PRInt32
709 nsTableCellMap::GetNumCellsOriginatingInCol(PRInt32 aColIndex) const
711 PRInt32 colCount = mCols.Length();
712 if ((aColIndex >= 0) && (aColIndex < colCount)) {
713 return mCols.ElementAt(aColIndex).mNumCellsOrig;
715 else {
716 NS_ERROR("nsCellMap::GetNumCellsOriginatingInCol - bad col index");
717 return 0;
721 #ifdef NS_DEBUG
722 void
723 nsTableCellMap::Dump(char* aString) const
725 if (aString)
726 printf("%s \n", aString);
727 printf("***** START TABLE CELL MAP DUMP ***** %p\n", (void*)this);
728 // output col info
729 PRInt32 colCount = mCols.Length();
730 printf ("cols array orig/span-> %p", (void*)this);
731 for (PRInt32 colX = 0; colX < colCount; colX++) {
732 const nsColInfo& colInfo = mCols.ElementAt(colX);
733 printf ("%d=%d/%d ", colX, colInfo.mNumCellsOrig, colInfo.mNumCellsSpan);
735 printf(" cols in cache %d\n", mTableFrame.GetColCache().Length());
736 nsCellMap* cellMap = mFirstMap;
737 while (cellMap) {
738 cellMap->Dump(nsnull != mBCInfo);
739 cellMap = cellMap->GetNextSibling();
741 if (nsnull != mBCInfo) {
742 printf("***** bottom borders *****\n");
743 nscoord size;
744 BCBorderOwner owner;
745 PRUint8 side;
746 PRBool segStart;
747 PRPackedBool bevel;
748 PRInt32 colIndex;
749 PRInt32 numCols = mBCInfo->mBottomBorders.Length();
750 for (PRInt32 i = 0; i <= 2; i++) {
752 printf("\n ");
753 for (colIndex = 0; colIndex < numCols; colIndex++) {
754 BCData& cd = mBCInfo->mBottomBorders.ElementAt(colIndex);
755 if (0 == i) {
756 size = cd.GetTopEdge(owner, segStart);
757 printf("t=%d%X%d ", size, owner, segStart);
759 else if (1 == i) {
760 size = cd.GetLeftEdge(owner, segStart);
761 printf("l=%d%X%d ", size, owner, segStart);
763 else {
764 size = cd.GetCorner(side, bevel);
765 printf("c=%d%X%d ", size, side, bevel);
768 BCData& cd = mBCInfo->mLowerRightCorner;
769 if (0 == i) {
770 size = cd.GetTopEdge(owner, segStart);
771 printf("t=%d%X%d ", size, owner, segStart);
773 else if (1 == i) {
774 size = cd.GetLeftEdge(owner, segStart);
775 printf("l=%d%X%d ", size, owner, segStart);
777 else {
778 size = cd.GetCorner(side, bevel);
779 printf("c=%d%X%d ", size, side, bevel);
782 printf("\n");
784 printf("***** END TABLE CELL MAP DUMP *****\n");
786 #endif
788 nsTableCellFrame*
789 nsTableCellMap::GetCellInfoAt(PRInt32 aRowIndex,
790 PRInt32 aColIndex,
791 PRBool* aOriginates,
792 PRInt32* aColSpan) const
794 PRInt32 rowIndex = aRowIndex;
795 nsCellMap* cellMap = mFirstMap;
796 while (cellMap) {
797 if (cellMap->GetRowCount() > rowIndex) {
798 return cellMap->GetCellInfoAt(*this, rowIndex, aColIndex, aOriginates, aColSpan);
800 rowIndex -= cellMap->GetRowCount();
801 cellMap = cellMap->GetNextSibling();
803 return nsnull;
806 PRInt32
807 nsTableCellMap::GetIndexByRowAndColumn(PRInt32 aRow, PRInt32 aColumn) const
809 PRInt32 index = 0;
811 PRInt32 colCount = mCols.Length();
812 PRInt32 rowIndex = aRow;
814 nsCellMap* cellMap = mFirstMap;
815 while (cellMap) {
816 PRInt32 rowCount = cellMap->GetRowCount();
817 if (rowIndex >= rowCount) {
818 // If the rowCount is less than the rowIndex, this means that the index is
819 // not within the current map. If so, get the index of the last cell in
820 // the last row.
821 rowIndex -= rowCount;
823 PRInt32 cellMapIdx = cellMap->GetHighestIndex(colCount);
824 if (cellMapIdx != -1)
825 index += cellMapIdx + 1;
827 } else {
828 // Index is in valid range for this cellmap, so get the index of rowIndex
829 // and aColumn.
830 PRInt32 cellMapIdx = cellMap->GetIndexByRowAndColumn(colCount, rowIndex,
831 aColumn);
832 if (cellMapIdx == -1)
833 return -1; // no cell at the given row and column.
835 index += cellMapIdx;
836 return index; // no need to look through further maps here
839 cellMap = cellMap->GetNextSibling();
842 return -1;
845 void
846 nsTableCellMap::GetRowAndColumnByIndex(PRInt32 aIndex,
847 PRInt32 *aRow, PRInt32 *aColumn) const
849 *aRow = -1;
850 *aColumn = -1;
852 PRInt32 colCount = mCols.Length();
854 PRInt32 previousRows = 0;
855 PRInt32 index = aIndex;
857 nsCellMap* cellMap = mFirstMap;
858 while (cellMap) {
859 PRInt32 rowCount = cellMap->GetRowCount();
860 // Determine the highest possible index in this map to see
861 // if wanted index is in here.
862 PRInt32 cellMapIdx = cellMap->GetHighestIndex(colCount);
863 if (cellMapIdx == -1) {
864 // The index is not within this map, increase the total row index
865 // accordingly.
866 previousRows += rowCount;
867 } else {
868 if (index > cellMapIdx) {
869 // The index is not within this map, so decrease it by the cellMapIdx
870 // determined index and increase the total row index accordingly.
871 index -= cellMapIdx + 1;
872 previousRows += rowCount;
873 } else {
874 cellMap->GetRowAndColumnByIndex(colCount, index, aRow, aColumn);
875 // If there were previous indexes, take them into account.
876 *aRow += previousRows;
877 return; // no need to look any further.
881 cellMap = cellMap->GetNextSibling();
885 PRBool nsTableCellMap::RowIsSpannedInto(PRInt32 aRowIndex,
886 PRInt32 aNumEffCols) const
888 PRInt32 rowIndex = aRowIndex;
889 nsCellMap* cellMap = mFirstMap;
890 while (cellMap) {
891 if (cellMap->GetRowCount() > rowIndex) {
892 return cellMap->RowIsSpannedInto(rowIndex, aNumEffCols);
894 rowIndex -= cellMap->GetRowCount();
895 cellMap = cellMap->GetNextSibling();
897 return PR_FALSE;
900 PRBool nsTableCellMap::RowHasSpanningCells(PRInt32 aRowIndex,
901 PRInt32 aNumEffCols) const
903 PRInt32 rowIndex = aRowIndex;
904 nsCellMap* cellMap = mFirstMap;
905 while (cellMap) {
906 if (cellMap->GetRowCount() > rowIndex) {
907 return cellMap->RowHasSpanningCells(rowIndex, aNumEffCols);
909 rowIndex -= cellMap->GetRowCount();
910 cellMap = cellMap->GetNextSibling();
912 return PR_FALSE;
915 PRBool nsTableCellMap::ColIsSpannedInto(PRInt32 aColIndex) const
917 PRBool result = PR_FALSE;
919 PRInt32 colCount = mCols.Length();
920 if ((aColIndex >= 0) && (aColIndex < colCount)) {
921 result = mCols.ElementAt(aColIndex).mNumCellsSpan != 0;
923 return result;
926 PRBool nsTableCellMap::ColHasSpanningCells(PRInt32 aColIndex) const
928 NS_PRECONDITION (aColIndex < GetColCount(), "bad col index arg");
929 nsCellMap* cellMap = mFirstMap;
930 while (cellMap) {
931 if (cellMap->ColHasSpanningCells(aColIndex)) {
932 return PR_TRUE;
934 cellMap = cellMap->GetNextSibling();
936 return PR_FALSE;
939 void nsTableCellMap::ExpandZeroColSpans()
941 mTableFrame.SetNeedColSpanExpansion(PR_FALSE); // mark the work done
942 mTableFrame.SetHasZeroColSpans(PR_FALSE); // reset the bit, if there is a
943 // zerospan it will be set again.
944 nsCellMap* cellMap = mFirstMap;
945 while (cellMap) {
946 cellMap->ExpandZeroColSpans(*this);
947 cellMap = cellMap->GetNextSibling();
951 void
952 nsTableCellMap::SetNotTopStart(PRUint8 aSide,
953 nsCellMap& aCellMap,
954 PRUint32 aRowIndex,
955 PRUint32 aColIndex,
956 PRBool aIsLowerRight)
958 if (!mBCInfo || aIsLowerRight) ABORT0();
960 BCCellData* cellData;
961 BCData* bcData = nsnull;
963 switch(aSide) {
964 case NS_SIDE_BOTTOM:
965 aRowIndex++;
966 case NS_SIDE_TOP:
967 cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex);
968 if (cellData) {
969 bcData = &cellData->mData;
971 else {
972 NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error");
973 // try the next row group
974 nsCellMap* cellMap = aCellMap.GetNextSibling();
975 if (cellMap) {
976 cellData = (BCCellData*)cellMap->GetDataAt(0, aColIndex);
977 if (cellData) {
978 bcData = &cellData->mData;
980 else {
981 bcData = GetBottomMostBorder(aColIndex);
985 break;
986 case NS_SIDE_RIGHT:
987 aColIndex++;
988 case NS_SIDE_LEFT:
989 cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex);
990 if (cellData) {
991 bcData = &cellData->mData;
993 else {
994 NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error");
995 bcData = GetRightMostBorder(aRowIndex);
997 break;
999 if (bcData) {
1000 bcData->SetTopStart(PR_FALSE);
1004 // store the aSide border segment at coord = (aRowIndex, aColIndex). For top/left, store
1005 // the info at coord. For bottom/left store it at the adjacent location so that it is
1006 // top/left at that location. If the new location is at the right or bottom edge of the
1007 // table, then store it one of the special arrays (right most borders, bottom most borders).
1008 void
1009 nsTableCellMap::SetBCBorderEdge(PRUint8 aSide,
1010 nsCellMap& aCellMap,
1011 PRUint32 aCellMapStart,
1012 PRUint32 aRowIndex,
1013 PRUint32 aColIndex,
1014 PRUint32 aLength,
1015 BCBorderOwner aOwner,
1016 nscoord aSize,
1017 PRBool aChanged)
1019 if (!mBCInfo) ABORT0();
1021 BCCellData* cellData;
1022 PRInt32 lastIndex, xIndex, yIndex;
1023 PRInt32 xPos = aColIndex;
1024 PRInt32 yPos = aRowIndex;
1025 PRInt32 rgYPos = aRowIndex - aCellMapStart;
1026 PRBool changed;
1028 switch(aSide) {
1029 case NS_SIDE_BOTTOM:
1030 rgYPos++;
1031 yPos++;
1032 case NS_SIDE_TOP:
1033 lastIndex = xPos + aLength - 1;
1034 for (xIndex = xPos; xIndex <= lastIndex; xIndex++) {
1035 changed = aChanged && (xIndex == xPos);
1036 BCData* bcData = nsnull;
1037 cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xIndex);
1038 if (!cellData) {
1039 PRInt32 numRgRows = aCellMap.GetRowCount();
1040 if (yPos < numRgRows) { // add a dead cell data
1041 nsRect damageArea;
1042 cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea); if (!cellData) ABORT0();
1044 else {
1045 NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error");
1046 // try the next non empty row group
1047 nsCellMap* cellMap = aCellMap.GetNextSibling();
1048 while (cellMap && (0 == cellMap->GetRowCount())) {
1049 cellMap = cellMap->GetNextSibling();
1051 if (cellMap) {
1052 cellData = (BCCellData*)cellMap->GetDataAt(0, xIndex);
1053 if (!cellData) { // add a dead cell
1054 nsRect damageArea;
1055 cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea);
1058 else { // must be at the end of the table
1059 bcData = GetBottomMostBorder(xIndex);
1063 if (!bcData && cellData) {
1064 bcData = &cellData->mData;
1066 if (bcData) {
1067 bcData->SetTopEdge(aOwner, aSize, changed);
1069 else NS_ERROR("Cellmap: Top edge not found");
1071 break;
1072 case NS_SIDE_RIGHT:
1073 xPos++;
1074 case NS_SIDE_LEFT:
1075 // since top, bottom borders were set, there should already be a cellData entry
1076 lastIndex = rgYPos + aLength - 1;
1077 for (yIndex = rgYPos; yIndex <= lastIndex; yIndex++) {
1078 changed = aChanged && (yIndex == rgYPos);
1079 cellData = (BCCellData*)aCellMap.GetDataAt(yIndex, xPos);
1080 if (cellData) {
1081 cellData->mData.SetLeftEdge(aOwner, aSize, changed);
1083 else {
1084 NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error");
1085 BCData* bcData = GetRightMostBorder(yIndex + aCellMapStart);
1086 if (bcData) {
1087 bcData->SetLeftEdge(aOwner, aSize, changed);
1089 else NS_ERROR("Cellmap: Left edge not found");
1092 break;
1096 // store corner info (aOwner, aSubSize, aBevel). For aCorner = eTopLeft, store the info at
1097 // (aRowIndex, aColIndex). For eTopRight, store it in the entry to the right where
1098 // it would be top left. For eBottomRight, store it in the entry to the bottom. etc.
1099 void
1100 nsTableCellMap::SetBCBorderCorner(Corner aCorner,
1101 nsCellMap& aCellMap,
1102 PRUint32 aCellMapStart,
1103 PRUint32 aRowIndex,
1104 PRUint32 aColIndex,
1105 PRUint8 aOwner,
1106 nscoord aSubSize,
1107 PRBool aBevel,
1108 PRBool aIsBottomRight)
1110 if (!mBCInfo) ABORT0();
1112 if (aIsBottomRight) {
1113 mBCInfo->mLowerRightCorner.SetCorner(aSubSize, aOwner, aBevel);
1114 return;
1117 PRInt32 xPos = aColIndex;
1118 PRInt32 yPos = aRowIndex;
1119 PRInt32 rgYPos = aRowIndex - aCellMapStart;
1121 if (eTopRight == aCorner) {
1122 xPos++;
1124 else if (eBottomRight == aCorner) {
1125 xPos++;
1126 rgYPos++;
1127 yPos++;
1129 else if (eBottomLeft == aCorner) {
1130 rgYPos++;
1131 yPos++;
1134 BCCellData* cellData = nsnull;
1135 BCData* bcData = nsnull;
1136 if (GetColCount() <= xPos) {
1137 NS_ASSERTION(xPos == GetColCount(), "program error");
1138 // at the right edge of the table as we checked the corner before
1139 NS_ASSERTION(!aIsBottomRight, "should be handled before");
1140 bcData = GetRightMostBorder(yPos);
1142 else {
1143 cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xPos);
1144 if (!cellData) {
1145 PRInt32 numRgRows = aCellMap.GetRowCount();
1146 if (yPos < numRgRows) { // add a dead cell data
1147 nsRect damageArea;
1148 cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea);
1150 else {
1151 // try the next non empty row group
1152 nsCellMap* cellMap = aCellMap.GetNextSibling();
1153 while (cellMap && (0 == cellMap->GetRowCount())) {
1154 cellMap = cellMap->GetNextSibling();
1156 if (cellMap) {
1157 cellData = (BCCellData*)cellMap->GetDataAt(0, xPos);
1158 if (!cellData) { // add a dead cell
1159 nsRect damageArea;
1160 cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea);
1163 else { // must be a the bottom of the table
1164 bcData = GetBottomMostBorder(xPos);
1169 if (!bcData && cellData) {
1170 bcData = &cellData->mData;
1172 if (bcData) {
1173 bcData->SetCorner(aSubSize, aOwner, aBevel);
1175 else NS_ERROR("program error: Corner not found");
1178 nsCellMap::nsCellMap(nsTableRowGroupFrame& aRowGroup, PRBool aIsBC)
1179 : mRows(8), mContentRowCount(0), mRowGroupFrame(&aRowGroup),
1180 mNextSibling(nsnull), mIsBC(aIsBC),
1181 mPresContext(aRowGroup.PresContext())
1183 MOZ_COUNT_CTOR(nsCellMap);
1184 NS_ASSERTION(mPresContext, "Must have prescontext");
1187 nsCellMap::~nsCellMap()
1189 MOZ_COUNT_DTOR(nsCellMap);
1191 PRUint32 mapRowCount = mRows.Length();
1192 for (PRUint32 rowX = 0; rowX < mapRowCount; rowX++) {
1193 CellDataArray &row = mRows[rowX];
1194 PRUint32 colCount = row.Length();
1195 for (PRUint32 colX = 0; colX < colCount; colX++) {
1196 DestroyCellData(row[colX]);
1201 /* static */
1202 nsresult
1203 nsCellMap::Init()
1205 NS_ASSERTION(!sEmptyRow, "How did that happen?");
1206 sEmptyRow = new nsCellMap::CellDataArray();
1207 NS_ENSURE_TRUE(sEmptyRow, NS_ERROR_OUT_OF_MEMORY);
1209 return NS_OK;
1212 /* static */
1213 void
1214 nsCellMap::Shutdown()
1216 delete sEmptyRow;
1217 sEmptyRow = nsnull;
1220 nsTableCellFrame*
1221 nsCellMap::GetCellFrame(PRInt32 aRowIndexIn,
1222 PRInt32 aColIndexIn,
1223 CellData& aData,
1224 PRBool aUseRowIfOverlap) const
1226 PRInt32 rowIndex = aRowIndexIn - aData.GetRowSpanOffset();
1227 PRInt32 colIndex = aColIndexIn - aData.GetColSpanOffset();
1228 if (aData.IsOverlap()) {
1229 if (aUseRowIfOverlap) {
1230 colIndex = aColIndexIn;
1232 else {
1233 rowIndex = aRowIndexIn;
1237 CellData* data =
1238 mRows.SafeElementAt(rowIndex, *sEmptyRow).SafeElementAt(colIndex);
1239 if (data) {
1240 return data->GetCellFrame();
1242 return nsnull;
1245 PRInt32
1246 nsCellMap::GetHighestIndex(PRInt32 aColCount)
1248 PRInt32 index = -1;
1249 PRInt32 rowCount = mRows.Length();
1250 for (PRInt32 rowIdx = 0; rowIdx < rowCount; rowIdx++) {
1251 const CellDataArray& row = mRows[rowIdx];
1253 for (PRInt32 colIdx = 0; colIdx < aColCount; colIdx++) {
1254 CellData* data = row.SafeElementAt(colIdx);
1255 // No data means row doesn't have more cells.
1256 if (!data)
1257 break;
1259 if (data->IsOrig())
1260 index++;
1264 return index;
1267 PRInt32
1268 nsCellMap::GetIndexByRowAndColumn(PRInt32 aColCount,
1269 PRInt32 aRow, PRInt32 aColumn) const
1271 if (PRUint32(aRow) >= mRows.Length())
1272 return -1;
1274 PRInt32 index = -1;
1275 PRInt32 lastColsIdx = aColCount - 1;
1277 // Find row index of the cell where row span is started.
1278 const CellDataArray& row = mRows[aRow];
1279 CellData* data = row.SafeElementAt(aColumn);
1280 PRInt32 origRow = data ? aRow - data->GetRowSpanOffset() : aRow;
1282 // Calculate cell index.
1283 for (PRInt32 rowIdx = 0; rowIdx <= origRow; rowIdx++) {
1284 const CellDataArray& row = mRows[rowIdx];
1285 PRInt32 colCount = (rowIdx == origRow) ? aColumn : lastColsIdx;
1287 for (PRInt32 colIdx = 0; colIdx <= colCount; colIdx++) {
1288 data = row.SafeElementAt(colIdx);
1289 // No data means row doesn't have more cells.
1290 if (!data)
1291 break;
1293 if (data->IsOrig())
1294 index++;
1298 // Given row and column don't point to the cell.
1299 if (!data)
1300 return -1;
1302 return index;
1305 void
1306 nsCellMap::GetRowAndColumnByIndex(PRInt32 aColCount, PRInt32 aIndex,
1307 PRInt32 *aRow, PRInt32 *aColumn) const
1309 *aRow = -1;
1310 *aColumn = -1;
1312 PRInt32 index = aIndex;
1313 PRInt32 rowCount = mRows.Length();
1315 for (PRInt32 rowIdx = 0; rowIdx < rowCount; rowIdx++) {
1316 const CellDataArray& row = mRows[rowIdx];
1318 for (PRInt32 colIdx = 0; colIdx < aColCount; colIdx++) {
1319 CellData* data = row.SafeElementAt(colIdx);
1321 // The row doesn't have more cells.
1322 if (!data)
1323 break;
1325 if (data->IsOrig())
1326 index--;
1328 if (index < 0) {
1329 *aRow = rowIdx;
1330 *aColumn = colIdx;
1331 return;
1337 PRBool nsCellMap::Grow(nsTableCellMap& aMap,
1338 PRInt32 aNumRows,
1339 PRInt32 aRowIndex)
1341 NS_ASSERTION(aNumRows >= 1, "Why are we calling this?");
1343 // Get the number of cols we want to use for preallocating the row arrays.
1344 PRInt32 numCols = aMap.GetColCount();
1345 if (numCols == 0) {
1346 numCols = 4;
1348 PRUint32 startRowIndex = (aRowIndex >= 0) ? aRowIndex : mRows.Length();
1349 NS_ASSERTION(startRowIndex <= mRows.Length(), "Missing grow call inbetween");
1351 return mRows.InsertElementsAt(startRowIndex, aNumRows, numCols) != nsnull;
1354 void nsCellMap::GrowRow(CellDataArray& aRow,
1355 PRInt32 aNumCols)
1358 // Have to have the cast to get the template to do the right thing.
1359 aRow.InsertElementsAt(aRow.Length(), aNumCols, (CellData*)nsnull);
1362 void
1363 nsCellMap::InsertRows(nsTableCellMap& aMap,
1364 nsTArray<nsTableRowFrame*>& aRows,
1365 PRInt32 aFirstRowIndex,
1366 PRBool aConsiderSpans,
1367 nsRect& aDamageArea)
1369 PRInt32 numCols = aMap.GetColCount();
1370 NS_ASSERTION(aFirstRowIndex >= 0, "nsCellMap::InsertRows called with negative rowIndex");
1371 if (PRUint32(aFirstRowIndex) > mRows.Length()) {
1372 // create (aFirstRowIndex - mRows.Length()) empty rows up to aFirstRowIndex
1373 PRInt32 numEmptyRows = aFirstRowIndex - mRows.Length();
1374 if (!Grow(aMap, numEmptyRows)) {
1375 return;
1379 if (!aConsiderSpans) {
1380 // update mContentRowCount, since non-empty rows will be added
1381 mContentRowCount = NS_MAX(aFirstRowIndex, mContentRowCount);
1382 ExpandWithRows(aMap, aRows, aFirstRowIndex, aDamageArea);
1383 return;
1386 // if any cells span into or out of the row being inserted, then rebuild
1387 PRBool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex,
1388 aFirstRowIndex, 0, numCols - 1);
1390 // update mContentRowCount, since non-empty rows will be added
1391 mContentRowCount = NS_MAX(aFirstRowIndex, mContentRowCount);
1393 // if any of the new cells span out of the new rows being added, then rebuild
1394 // XXX it would be better to only rebuild the portion of the map that follows the new rows
1395 if (!spansCauseRebuild && (PRUint32(aFirstRowIndex) < mRows.Length())) {
1396 spansCauseRebuild = CellsSpanOut(aRows);
1399 if (spansCauseRebuild) {
1400 aMap.RebuildConsideringRows(this, aFirstRowIndex, &aRows, 0, aDamageArea);
1402 else {
1403 ExpandWithRows(aMap, aRows, aFirstRowIndex, aDamageArea);
1407 void
1408 nsCellMap::RemoveRows(nsTableCellMap& aMap,
1409 PRInt32 aFirstRowIndex,
1410 PRInt32 aNumRowsToRemove,
1411 PRBool aConsiderSpans,
1412 nsRect& aDamageArea)
1414 PRInt32 numRows = mRows.Length();
1415 PRInt32 numCols = aMap.GetColCount();
1417 if (aFirstRowIndex >= numRows) {
1418 // reduce the content based row count based on the function arguments
1419 // as they are known to be real rows even if the cell map did not create
1420 // rows for them before.
1421 mContentRowCount -= aNumRowsToRemove;
1422 return;
1424 if (!aConsiderSpans) {
1425 ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea);
1426 return;
1428 PRInt32 endRowIndex = aFirstRowIndex + aNumRowsToRemove - 1;
1429 if (endRowIndex >= numRows) {
1430 NS_ERROR("nsCellMap::RemoveRows tried to remove too many rows");
1431 endRowIndex = numRows - 1;
1433 PRBool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex, endRowIndex,
1434 0, numCols - 1);
1436 if (spansCauseRebuild) {
1437 aMap.RebuildConsideringRows(this, aFirstRowIndex, nsnull, aNumRowsToRemove, aDamageArea);
1439 else {
1440 ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea);
1447 CellData*
1448 nsCellMap::AppendCell(nsTableCellMap& aMap,
1449 nsTableCellFrame* aCellFrame,
1450 PRInt32 aRowIndex,
1451 PRBool aRebuildIfNecessary,
1452 nsRect& aDamageArea,
1453 PRInt32* aColToBeginSearch)
1455 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1456 PRInt32 origNumMapRows = mRows.Length();
1457 PRInt32 origNumCols = aMap.GetColCount();
1458 PRBool zeroRowSpan = PR_FALSE;
1459 PRInt32 rowSpan = (aCellFrame) ? GetRowSpanForNewCell(aCellFrame, aRowIndex,
1460 zeroRowSpan) : 1;
1461 // add new rows if necessary
1462 PRInt32 endRowIndex = aRowIndex + rowSpan - 1;
1463 if (endRowIndex >= origNumMapRows) {
1464 // XXXbz handle allocation failures?
1465 Grow(aMap, 1 + endRowIndex - origNumMapRows);
1468 // get the first null or dead CellData in the desired row. It will equal origNumCols if there are none
1469 CellData* origData = nsnull;
1470 PRInt32 startColIndex = 0;
1471 if (aColToBeginSearch)
1472 startColIndex = *aColToBeginSearch;
1473 for (; startColIndex < origNumCols; startColIndex++) {
1474 CellData* data = GetDataAt(aRowIndex, startColIndex);
1475 if (!data)
1476 break;
1477 if (data->IsDead()) {
1478 origData = data;
1479 break;
1481 if (data->IsZeroColSpan() ) {
1482 // appending a cell collapses zerospans.
1483 CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex);
1484 // ask again for the data as it should be modified
1485 origData = GetDataAt(aRowIndex, startColIndex);
1486 NS_ASSERTION(origData->IsDead(),
1487 "The cellposition should have been cleared");
1488 break;
1491 // We found the place to append the cell, when the next cell is appended
1492 // the next search does not need to duplicate the search but can start
1493 // just at the next cell.
1494 if (aColToBeginSearch)
1495 *aColToBeginSearch = startColIndex + 1;
1497 PRBool zeroColSpan = PR_FALSE;
1498 PRInt32 colSpan = (aCellFrame) ?
1499 GetColSpanForNewCell(*aCellFrame, zeroColSpan) : 1;
1500 if (zeroColSpan) {
1501 aMap.mTableFrame.SetHasZeroColSpans(PR_TRUE);
1502 aMap.mTableFrame.SetNeedColSpanExpansion(PR_TRUE);
1505 // if the new cell could potentially span into other rows and collide with
1506 // originating cells there, we will play it safe and just rebuild the map
1507 if (aRebuildIfNecessary && (aRowIndex < mContentRowCount - 1) && (rowSpan > 1)) {
1508 nsAutoTArray<nsTableCellFrame*, 1> newCellArray;
1509 newCellArray.AppendElement(aCellFrame);
1510 aMap.RebuildConsideringCells(this, &newCellArray, aRowIndex, startColIndex, PR_TRUE, aDamageArea);
1511 return origData;
1513 mContentRowCount = NS_MAX(mContentRowCount, aRowIndex + 1);
1515 // add new cols to the table map if necessary
1516 PRInt32 endColIndex = startColIndex + colSpan - 1;
1517 if (endColIndex >= origNumCols) {
1518 NS_ASSERTION(aCellFrame, "dead cells should not require new columns");
1519 aMap.AddColsAtEnd(1 + endColIndex - origNumCols);
1522 // Setup CellData for this cell
1523 if (origData) {
1524 NS_ASSERTION(origData->IsDead(), "replacing a non dead cell is a memory leak");
1525 if (aCellFrame) { // do nothing to replace a dead cell with a dead cell
1526 origData->Init(aCellFrame);
1527 // we are replacing a dead cell, increase the number of cells
1528 // originating at this column
1529 nsColInfo* colInfo = aMap.GetColInfoAt(startColIndex);
1530 NS_ASSERTION(colInfo, "access to a non existing column");
1531 if (colInfo) {
1532 colInfo->mNumCellsOrig++;
1536 else {
1537 origData = AllocCellData(aCellFrame);
1538 if (!origData) ABORT1(origData);
1539 SetDataAt(aMap, *origData, aRowIndex, startColIndex);
1542 SetDamageArea(startColIndex, aRowIndex, 1 + endColIndex - startColIndex, 1 + endRowIndex - aRowIndex, aDamageArea);
1544 if (!aCellFrame) {
1545 return origData;
1548 // initialize the cell frame
1549 aCellFrame->SetColIndex(startColIndex);
1551 // Create CellData objects for the rows that this cell spans. Set
1552 // their mOrigCell to nsnull and their mSpanData to point to data.
1553 for (PRInt32 rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1554 // The row at rowX will need to have at least endColIndex columns
1555 mRows[rowX].SetCapacity(endColIndex);
1556 for (PRInt32 colX = startColIndex; colX <= endColIndex; colX++) {
1557 if ((rowX != aRowIndex) || (colX != startColIndex)) { // skip orig cell data done above
1558 CellData* cellData = GetDataAt(rowX, colX);
1559 if (cellData) {
1560 if (cellData->IsOrig()) {
1561 NS_ERROR("cannot overlap originating cell");
1562 continue;
1564 if (rowX > aRowIndex) { // row spanning into cell
1565 if (cellData->IsRowSpan()) {
1566 // do nothing, this can be caused by rowspan which is overlapped
1567 // by a another cell with a rowspan and a colspan
1569 else {
1570 cellData->SetRowSpanOffset(rowX - aRowIndex);
1571 if (zeroRowSpan) {
1572 cellData->SetZeroRowSpan(PR_TRUE);
1576 if (colX > startColIndex) { // col spanning into cell
1577 if (!cellData->IsColSpan()) {
1578 if (cellData->IsRowSpan()) {
1579 cellData->SetOverlap(PR_TRUE);
1581 cellData->SetColSpanOffset(colX - startColIndex);
1582 if (zeroColSpan) {
1583 cellData->SetZeroColSpan(PR_TRUE);
1586 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1587 colInfo->mNumCellsSpan++;
1591 else {
1592 cellData = AllocCellData(nsnull);
1593 if (!cellData) return origData;
1594 if (rowX > aRowIndex) {
1595 cellData->SetRowSpanOffset(rowX - aRowIndex);
1596 if (zeroRowSpan) {
1597 cellData->SetZeroRowSpan(PR_TRUE);
1600 if (colX > startColIndex) {
1601 cellData->SetColSpanOffset(colX - startColIndex);
1602 if (zeroColSpan) {
1603 cellData->SetZeroColSpan(PR_TRUE);
1606 SetDataAt(aMap, *cellData, rowX, colX);
1611 #ifdef DEBUG_TABLE_CELLMAP
1612 printf("appended cell=%p row=%d \n", aCellFrame, aRowIndex);
1613 aMap.Dump();
1614 #endif
1615 return origData;
1618 void nsCellMap::CollapseZeroColSpan(nsTableCellMap& aMap,
1619 CellData* aOrigData,
1620 PRInt32 aRowIndex,
1621 PRInt32 aColIndex)
1623 // if after a colspan = 0 cell another cell is appended in a row the html 4
1624 // spec is already violated. In principle one should then append the cell
1625 // after the last column but then the zero spanning cell would also have
1626 // to grow. The only plausible way to break this cycle is ignore the zero
1627 // colspan and reset the cell to colspan = 1.
1629 NS_ASSERTION(aOrigData && aOrigData->IsZeroColSpan(),
1630 "zero colspan should have been passed");
1631 // find the originating cellframe
1632 nsTableCellFrame* cell = GetCellFrame(aRowIndex, aColIndex, *aOrigData, PR_TRUE);
1633 NS_ASSERTION(cell, "originating cell not found");
1635 // find the clearing region
1636 PRInt32 startRowIndex = aRowIndex - aOrigData->GetRowSpanOffset();
1637 PRBool zeroSpan;
1638 PRInt32 rowSpan = GetRowSpanForNewCell(cell, startRowIndex, zeroSpan);
1639 PRInt32 endRowIndex = startRowIndex + rowSpan;
1641 PRInt32 origColIndex = aColIndex - aOrigData->GetColSpanOffset();
1642 PRInt32 endColIndex = origColIndex +
1643 GetEffectiveColSpan(aMap, startRowIndex,
1644 origColIndex, zeroSpan);
1645 for (PRInt32 colX = origColIndex +1; colX < endColIndex; colX++) {
1646 // Start the collapse just after the originating cell, since
1647 // we're basically making the originating cell act as if it
1648 // has colspan="1".
1649 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1650 colInfo->mNumCellsSpan -= rowSpan;
1652 for (PRInt32 rowX = startRowIndex; rowX < endRowIndex; rowX++)
1654 CellData* data = mRows[rowX][colX];
1655 NS_ASSERTION(data->IsZeroColSpan(),
1656 "Overwriting previous data - memory leak");
1657 data->Init(nsnull); // mark the cell as a dead cell.
1662 PRBool nsCellMap::CellsSpanOut(nsTArray<nsTableRowFrame*>& aRows) const
1664 PRInt32 numNewRows = aRows.Length();
1665 for (PRInt32 rowX = 0; rowX < numNewRows; rowX++) {
1666 nsIFrame* rowFrame = (nsIFrame *) aRows.ElementAt(rowX);
1667 nsIFrame* childFrame = rowFrame->GetFirstChild(nsnull);
1668 while (childFrame) {
1669 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
1670 if (cellFrame) {
1671 PRBool zeroSpan;
1672 PRInt32 rowSpan = GetRowSpanForNewCell(cellFrame, rowX, zeroSpan);
1673 if (rowX + rowSpan > numNewRows) {
1674 return PR_TRUE;
1677 childFrame = childFrame->GetNextSibling();
1680 return PR_FALSE;
1683 // return PR_TRUE if any cells have rows spans into or out of the region
1684 // defined by the row and col indices or any cells have colspans into the region
1685 PRBool nsCellMap::CellsSpanInOrOut(PRInt32 aStartRowIndex,
1686 PRInt32 aEndRowIndex,
1687 PRInt32 aStartColIndex,
1688 PRInt32 aEndColIndex) const
1691 * this routine will watch the cells adjacent to the region or at the edge
1692 * they are marked with *. The routine will verify whether they span in or
1693 * are spanned out.
1695 * startCol endCol
1696 * r1c1 r1c2 r1c3 r1c4 r1c5 r1rc6 r1c7
1697 * startrow r2c1 r2c2 *r2c3 *r2c4 *r2c5 *r2rc6 r2c7
1698 * endrow r3c1 r3c2 *r3c3 r3c4 r3c5 *r3rc6 r3c7
1699 * r4c1 r4c2 *r4c3 *r4c4 *r4c5 r4rc6 r4c7
1700 * r5c1 r5c2 r5c3 r5c4 r5c5 r5rc6 r5c7
1703 PRInt32 numRows = mRows.Length(); // use the cellmap rows to determine the
1704 // current cellmap extent.
1705 for (PRInt32 colX = aStartColIndex; colX <= aEndColIndex; colX++) {
1706 CellData* cellData;
1707 if (aStartRowIndex > 0) {
1708 cellData = GetDataAt(aStartRowIndex, colX);
1709 if (cellData && (cellData->IsRowSpan())) {
1710 return PR_TRUE; // there is a row span into the region
1712 if ((aStartRowIndex >= mContentRowCount) && (mContentRowCount > 0)) {
1713 cellData = GetDataAt(mContentRowCount - 1, colX);
1714 if (cellData && cellData->IsZeroRowSpan()) {
1715 return PR_TRUE; // When we expand the zerospan it'll span into our row
1719 if (aEndRowIndex < numRows - 1) { // is there anything below aEndRowIndex
1720 cellData = GetDataAt(aEndRowIndex + 1, colX);
1721 if ((cellData) && (cellData->IsRowSpan())) {
1722 return PR_TRUE; // there is a row span out of the region
1725 else {
1726 cellData = GetDataAt(aEndRowIndex, colX);
1727 if ((cellData) && (cellData->IsRowSpan()) && (mContentRowCount < numRows)) {
1728 return PR_TRUE; // this cell might be the cause of a dead row
1732 if (aStartColIndex > 0) {
1733 for (PRInt32 rowX = aStartRowIndex; rowX <= aEndRowIndex; rowX++) {
1734 CellData* cellData = GetDataAt(rowX, aStartColIndex);
1735 if (cellData && (cellData->IsColSpan())) {
1736 return PR_TRUE; // there is a col span into the region
1738 cellData = GetDataAt(rowX, aEndColIndex + 1);
1739 if (cellData && (cellData->IsColSpan())) {
1740 return PR_TRUE; // there is a col span out of the region
1744 return PR_FALSE;
1747 void nsCellMap::InsertCells(nsTableCellMap& aMap,
1748 nsTArray<nsTableCellFrame*>& aCellFrames,
1749 PRInt32 aRowIndex,
1750 PRInt32 aColIndexBefore,
1751 nsRect& aDamageArea)
1753 if (aCellFrames.Length() == 0) return;
1754 NS_ASSERTION(aColIndexBefore >= -1, "index out of range");
1755 PRInt32 numCols = aMap.GetColCount();
1756 if (aColIndexBefore >= numCols) {
1757 NS_ERROR("Inserting instead of appending cells indicates a serious cellmap error");
1758 aColIndexBefore = numCols - 1;
1761 // get the starting col index of the 1st new cells
1762 PRInt32 startColIndex;
1763 for (startColIndex = aColIndexBefore + 1; startColIndex < numCols; startColIndex++) {
1764 CellData* data = GetDataAt(aRowIndex, startColIndex);
1765 if (!data || data->IsOrig() || data->IsDead()) {
1766 // // Not a span. Stop.
1767 break;
1769 if (data->IsZeroColSpan()) {
1770 // Zero colspans collapse. Stop in this case too.
1771 CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex);
1772 break;
1776 // record whether inserted cells are going to cause complications due
1777 // to existing row spans, col spans or table sizing.
1778 PRBool spansCauseRebuild = PR_FALSE;
1780 // check that all cells have the same row span
1781 PRInt32 numNewCells = aCellFrames.Length();
1782 PRBool zeroRowSpan = PR_FALSE;
1783 PRInt32 rowSpan = 0;
1784 for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) {
1785 nsTableCellFrame* cell = aCellFrames.ElementAt(cellX);
1786 PRInt32 rowSpan2 = GetRowSpanForNewCell(cell, aRowIndex, zeroRowSpan);
1787 if (rowSpan == 0) {
1788 rowSpan = rowSpan2;
1790 else if (rowSpan != rowSpan2) {
1791 spansCauseRebuild = PR_TRUE;
1792 break;
1796 // check if the new cells will cause the table to add more rows
1797 if (!spansCauseRebuild) {
1798 if (mRows.Length() < PRUint32(aRowIndex + rowSpan)) {
1799 spansCauseRebuild = PR_TRUE;
1803 if (!spansCauseRebuild) {
1804 spansCauseRebuild = CellsSpanInOrOut(aRowIndex, aRowIndex + rowSpan - 1,
1805 startColIndex, numCols - 1);
1808 if (spansCauseRebuild) {
1809 aMap.RebuildConsideringCells(this, &aCellFrames, aRowIndex, startColIndex, PR_TRUE, aDamageArea);
1811 else {
1812 ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan, zeroRowSpan, aDamageArea);
1816 void
1817 nsCellMap::ExpandWithRows(nsTableCellMap& aMap,
1818 nsTArray<nsTableRowFrame*>& aRowFrames,
1819 PRInt32 aStartRowIndexIn,
1820 nsRect& aDamageArea)
1822 PRInt32 startRowIndex = (aStartRowIndexIn >= 0) ? aStartRowIndexIn : 0;
1823 NS_ASSERTION(PRUint32(startRowIndex) <= mRows.Length(), "caller should have grown cellmap before");
1825 PRInt32 numNewRows = aRowFrames.Length();
1826 mContentRowCount += numNewRows;
1828 PRInt32 endRowIndex = startRowIndex + numNewRows - 1;
1830 // shift the rows after startRowIndex down and insert empty rows that will
1831 // be filled via the AppendCell call below
1832 if (!Grow(aMap, numNewRows, startRowIndex)) {
1833 return;
1837 PRInt32 newRowIndex = 0;
1838 for (PRInt32 rowX = startRowIndex; rowX <= endRowIndex; rowX++) {
1839 nsTableRowFrame* rFrame = aRowFrames.ElementAt(newRowIndex);
1840 // append cells
1841 nsIFrame* cFrame = rFrame->GetFirstChild(nsnull);
1842 PRInt32 colIndex = 0;
1843 while (cFrame) {
1844 nsTableCellFrame *cellFrame = do_QueryFrame(cFrame);
1845 if (cellFrame) {
1846 AppendCell(aMap, cellFrame, rowX, PR_FALSE, aDamageArea, &colIndex);
1848 cFrame = cFrame->GetNextSibling();
1850 newRowIndex++;
1853 SetDamageArea(0, startRowIndex, aMap.GetColCount(), 1 + endRowIndex - startRowIndex, aDamageArea);
1856 void nsCellMap::ExpandWithCells(nsTableCellMap& aMap,
1857 nsTArray<nsTableCellFrame*>& aCellFrames,
1858 PRInt32 aRowIndex,
1859 PRInt32 aColIndex,
1860 PRInt32 aRowSpan, // same for all cells
1861 PRBool aRowSpanIsZero,
1862 nsRect& aDamageArea)
1864 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1865 PRInt32 endRowIndex = aRowIndex + aRowSpan - 1;
1866 PRInt32 startColIndex = aColIndex;
1867 PRInt32 endColIndex = aColIndex;
1868 PRInt32 numCells = aCellFrames.Length();
1869 PRInt32 totalColSpan = 0;
1871 // add cellData entries for the space taken up by the new cells
1872 for (PRInt32 cellX = 0; cellX < numCells; cellX++) {
1873 nsTableCellFrame* cellFrame = aCellFrames.ElementAt(cellX);
1874 CellData* origData = AllocCellData(cellFrame); // the originating cell
1875 if (!origData) return;
1877 // set the starting and ending col index for the new cell
1878 PRBool zeroColSpan = PR_FALSE;
1879 PRInt32 colSpan = GetColSpanForNewCell(*cellFrame, zeroColSpan);
1880 if (zeroColSpan) {
1881 aMap.mTableFrame.SetHasZeroColSpans(PR_TRUE);
1882 aMap.mTableFrame.SetNeedColSpanExpansion(PR_TRUE);
1884 totalColSpan += colSpan;
1885 if (cellX == 0) {
1886 endColIndex = aColIndex + colSpan - 1;
1888 else {
1889 startColIndex = endColIndex + 1;
1890 endColIndex = startColIndex + colSpan - 1;
1893 // add the originating cell data and any cell data corresponding to row/col spans
1894 for (PRInt32 rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1895 CellDataArray& row = mRows[rowX];
1896 // Pre-allocate all the cells we'll need in this array, setting
1897 // them to null.
1898 // Have to have the cast to get the template to do the right thing.
1899 PRInt32 insertionIndex = row.Length();
1900 if (insertionIndex > startColIndex) {
1901 insertionIndex = startColIndex;
1903 if (!row.InsertElementsAt(insertionIndex, endColIndex - insertionIndex + 1,
1904 (CellData*)nsnull) &&
1905 rowX == aRowIndex) {
1906 // Failed to insert the slots, and this is the very first row. That
1907 // means that we need to clean up |origData| before returning, since
1908 // the cellmap doesn't own it yet.
1909 DestroyCellData(origData);
1910 return;
1913 for (PRInt32 colX = startColIndex; colX <= endColIndex; colX++) {
1914 CellData* data = origData;
1915 if ((rowX != aRowIndex) || (colX != startColIndex)) {
1916 data = AllocCellData(nsnull);
1917 if (!data) return;
1918 if (rowX > aRowIndex) {
1919 data->SetRowSpanOffset(rowX - aRowIndex);
1920 if (aRowSpanIsZero) {
1921 data->SetZeroRowSpan(PR_TRUE);
1924 if (colX > startColIndex) {
1925 data->SetColSpanOffset(colX - startColIndex);
1926 if (zeroColSpan) {
1927 data->SetZeroColSpan(PR_TRUE);
1931 SetDataAt(aMap, *data, rowX, colX);
1934 cellFrame->SetColIndex(startColIndex);
1936 PRInt32 damageHeight = NS_MIN(GetRowGroup()->GetRowCount() - aRowIndex, aRowSpan);
1937 SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, damageHeight, aDamageArea);
1939 PRInt32 rowX;
1941 // update the row and col info due to shifting
1942 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1943 CellDataArray& row = mRows[rowX];
1944 PRUint32 numCols = row.Length();
1945 PRUint32 colX;
1946 for (colX = aColIndex + totalColSpan; colX < numCols; colX++) {
1947 CellData* data = row[colX];
1948 if (data) {
1949 // increase the origin and span counts beyond the spanned cols
1950 if (data->IsOrig()) {
1951 // a cell that gets moved needs adjustment as well as it new orignating col
1952 data->GetCellFrame()->SetColIndex(colX);
1953 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1954 colInfo->mNumCellsOrig++;
1956 if (data->IsColSpan()) {
1957 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1958 colInfo->mNumCellsSpan++;
1961 // decrease the origin and span counts within the spanned cols
1962 PRInt32 colX2 = colX - totalColSpan;
1963 nsColInfo* colInfo2 = aMap.GetColInfoAt(colX2);
1964 if (data->IsOrig()) {
1965 // the old originating col of a moved cell needs adjustment
1966 colInfo2->mNumCellsOrig--;
1968 if (data->IsColSpan()) {
1969 colInfo2->mNumCellsSpan--;
1976 void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap,
1977 PRInt32 aStartRowIndex,
1978 PRInt32 aNumRowsToRemove,
1979 nsRect& aDamageArea)
1981 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1982 PRInt32 endRowIndex = aStartRowIndex + aNumRowsToRemove - 1;
1983 PRUint32 colCount = aMap.GetColCount();
1984 for (PRInt32 rowX = endRowIndex; rowX >= aStartRowIndex; --rowX) {
1985 CellDataArray& row = mRows[rowX];
1986 PRUint32 colX;
1987 for (colX = 0; colX < colCount; colX++) {
1988 CellData* data = row.SafeElementAt(colX);
1989 if (data) {
1990 // Adjust the column counts.
1991 if (data->IsOrig()) {
1992 // Decrement the column count.
1993 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1994 colInfo->mNumCellsOrig--;
1996 // colspan=0 is only counted as a spanned cell in the 1st col it spans
1997 else if (data->IsColSpan()) {
1998 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1999 colInfo->mNumCellsSpan--;
2004 PRUint32 rowLength = row.Length();
2005 // Delete our row information.
2006 for (colX = 0; colX < rowLength; colX++) {
2007 DestroyCellData(row[colX]);
2010 mRows.RemoveElementAt(rowX);
2012 // Decrement our row and next available index counts.
2013 mContentRowCount--;
2015 aMap.RemoveColsAtEnd();
2017 SetDamageArea(0, aStartRowIndex, aMap.GetColCount(), 0, aDamageArea);
2020 PRInt32 nsCellMap::GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd,
2021 PRBool& aIsZeroColSpan) const
2023 aIsZeroColSpan = PR_FALSE;
2024 PRInt32 colSpan = aCellFrameToAdd.GetColSpan();
2025 if (0 == colSpan) {
2026 colSpan = 1; // set the min colspan it will be expanded later
2027 aIsZeroColSpan = PR_TRUE;
2029 return colSpan;
2032 PRInt32 nsCellMap::GetEffectiveColSpan(const nsTableCellMap& aMap,
2033 PRInt32 aRowIndex,
2034 PRInt32 aColIndex,
2035 PRBool& aZeroColSpan) const
2037 PRInt32 numColsInTable = aMap.GetColCount();
2038 aZeroColSpan = PR_FALSE;
2039 PRInt32 colSpan = 1;
2040 if (PRUint32(aRowIndex) >= mRows.Length()) {
2041 return colSpan;
2044 const CellDataArray& row = mRows[aRowIndex];
2045 PRInt32 colX;
2046 CellData* data;
2047 PRInt32 maxCols = numColsInTable;
2048 PRBool hitOverlap = PR_FALSE; // XXX this is not ever being set to PR_TRUE
2049 for (colX = aColIndex + 1; colX < maxCols; colX++) {
2050 data = row.SafeElementAt(colX);
2051 if (data) {
2052 // for an overlapping situation get the colspan from the originating cell and
2053 // use that as the max number of cols to iterate. Since this is rare, only
2054 // pay the price of looking up the cell's colspan here.
2055 if (!hitOverlap && data->IsOverlap()) {
2056 CellData* origData = row.SafeElementAt(aColIndex);
2057 if (origData && origData->IsOrig()) {
2058 nsTableCellFrame* cellFrame = origData->GetCellFrame();
2059 if (cellFrame) {
2060 // possible change the number of colums to iterate
2061 maxCols = NS_MIN(aColIndex + cellFrame->GetColSpan(), maxCols);
2062 if (colX >= maxCols)
2063 break;
2067 if (data->IsColSpan()) {
2068 colSpan++;
2069 if (data->IsZeroColSpan()) {
2070 aZeroColSpan = PR_TRUE;
2073 else {
2074 break;
2077 else break;
2079 return colSpan;
2082 PRInt32
2083 nsCellMap::GetRowSpanForNewCell(nsTableCellFrame* aCellFrameToAdd,
2084 PRInt32 aRowIndex,
2085 PRBool& aIsZeroRowSpan) const
2087 aIsZeroRowSpan = PR_FALSE;
2088 PRInt32 rowSpan = aCellFrameToAdd->GetRowSpan();
2089 if (0 == rowSpan) {
2090 // Use a min value of 2 for a zero rowspan to make computations easier
2091 // elsewhere. Zero rowspans are only content dependent!
2092 rowSpan = NS_MAX(2, mContentRowCount - aRowIndex);
2093 aIsZeroRowSpan = PR_TRUE;
2095 return rowSpan;
2098 PRBool nsCellMap::HasMoreThanOneCell(PRInt32 aRowIndex) const
2100 const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow);
2101 PRUint32 maxColIndex = row.Length();
2102 PRUint32 count = 0;
2103 PRUint32 colIndex;
2104 for (colIndex = 0; colIndex < maxColIndex; colIndex++) {
2105 CellData* cellData = row[colIndex];
2106 if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan()))
2107 count++;
2108 if (count > 1)
2109 return PR_TRUE;
2111 return PR_FALSE;
2114 PRInt32
2115 nsCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const
2117 const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow);
2118 PRUint32 count = 0;
2119 PRUint32 maxColIndex = row.Length();
2120 PRUint32 colIndex;
2121 for (colIndex = 0; colIndex < maxColIndex; colIndex++) {
2122 CellData* cellData = row[colIndex];
2123 if (cellData && cellData->IsOrig())
2124 count++;
2126 return count;
2129 PRInt32 nsCellMap::GetRowSpan(PRInt32 aRowIndex,
2130 PRInt32 aColIndex,
2131 PRBool aGetEffective) const
2133 PRInt32 rowSpan = 1;
2134 PRInt32 rowCount = (aGetEffective) ? mContentRowCount : mRows.Length();
2135 PRInt32 rowX;
2136 for (rowX = aRowIndex + 1; rowX < rowCount; rowX++) {
2137 CellData* data = GetDataAt(rowX, aColIndex);
2138 if (data) {
2139 if (data->IsRowSpan()) {
2140 rowSpan++;
2142 else {
2143 break;
2146 else break;
2148 return rowSpan;
2151 void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap,
2152 nsTableCellFrame& aCellFrame,
2153 PRInt32 aRowIndex,
2154 PRInt32 aColIndex,
2155 nsRect& aDamageArea)
2157 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2158 PRUint32 colX, rowX;
2160 // get the rowspan and colspan from the cell map since the content may have changed
2161 PRBool zeroColSpan;
2162 PRUint32 numCols = aMap.GetColCount();
2163 PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, PR_FALSE);
2164 PRUint32 colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan);
2165 PRUint32 endRowIndex = aRowIndex + rowSpan - 1;
2166 PRUint32 endColIndex = aColIndex + colSpan - 1;
2168 SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, 1 + endRowIndex - aRowIndex, aDamageArea);
2170 if (aMap.mTableFrame.HasZeroColSpans()) {
2171 aMap.mTableFrame.SetNeedColSpanExpansion(PR_TRUE);
2174 // adjust the col counts due to the deleted cell before removing it
2175 for (colX = aColIndex; colX <= endColIndex; colX++) {
2176 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2177 if (colX == PRUint32(aColIndex)) {
2178 colInfo->mNumCellsOrig--;
2180 else {
2181 colInfo->mNumCellsSpan--;
2185 // remove the deleted cell and cellData entries for it
2186 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
2187 CellDataArray& row = mRows[rowX];
2189 // endIndexForRow points at the first slot we don't want to clean up. This
2190 // makes the aColIndex == 0 case work right with our unsigned int colX.
2191 NS_ASSERTION(endColIndex + 1 <= row.Length(), "span beyond the row size!");
2192 PRUint32 endIndexForRow = NS_MIN(endColIndex + 1, row.Length());
2194 // Since endIndexForRow <= row.Length(), enough to compare aColIndex to it.
2195 if (PRUint32(aColIndex) < endIndexForRow) {
2196 for (colX = endIndexForRow; colX > PRUint32(aColIndex); colX--) {
2197 DestroyCellData(row[colX-1]);
2199 row.RemoveElementsAt(aColIndex, endIndexForRow - aColIndex);
2203 numCols = aMap.GetColCount();
2205 // update the row and col info due to shifting
2206 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
2207 CellDataArray& row = mRows[rowX];
2208 for (colX = aColIndex; colX < numCols - colSpan; colX++) {
2209 CellData* data = row.SafeElementAt(colX);
2210 if (data) {
2211 if (data->IsOrig()) {
2212 // a cell that gets moved to the left needs adjustment in its new location
2213 data->GetCellFrame()->SetColIndex(colX);
2214 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2215 colInfo->mNumCellsOrig++;
2216 // a cell that gets moved to the left needs adjustment in its old location
2217 colInfo = aMap.GetColInfoAt(colX + colSpan);
2218 if (colInfo) {
2219 colInfo->mNumCellsOrig--;
2223 else if (data->IsColSpan()) {
2224 // a cell that gets moved to the left needs adjustment
2225 // in its new location
2226 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2227 colInfo->mNumCellsSpan++;
2228 // a cell that gets moved to the left needs adjustment
2229 // in its old location
2230 colInfo = aMap.GetColInfoAt(colX + colSpan);
2231 if (colInfo) {
2232 colInfo->mNumCellsSpan--;
2238 aMap.RemoveColsAtEnd();
2241 void
2242 nsCellMap::RebuildConsideringRows(nsTableCellMap& aMap,
2243 PRInt32 aStartRowIndex,
2244 nsTArray<nsTableRowFrame*>* aRowsToInsert,
2245 PRInt32 aNumRowsToRemove,
2246 nsRect& aDamageArea)
2248 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2249 // copy the old cell map into a new array
2250 PRUint32 numOrigRows = mRows.Length();
2251 nsTArray<CellDataArray> origRows;
2252 mRows.SwapElements(origRows);
2254 PRInt32 rowNumberChange;
2255 if (aRowsToInsert) {
2256 rowNumberChange = aRowsToInsert->Length();
2257 } else {
2258 rowNumberChange = -aNumRowsToRemove;
2261 // adjust mContentRowCount based on the function arguments as they are known to
2262 // be real rows.
2263 mContentRowCount += rowNumberChange;
2264 NS_ASSERTION(mContentRowCount >= 0, "previous mContentRowCount was wrong");
2265 // mRows is empty now. Grow it to the size we expect it to have.
2266 if (mContentRowCount) {
2267 if (!Grow(aMap, mContentRowCount)) {
2268 // Bail, I guess... Not sure what else we can do here.
2269 return;
2273 // aStartRowIndex might be after all existing rows so we should limit the
2274 // copy to the amount of exisiting rows
2275 PRUint32 copyEndRowIndex = NS_MIN(numOrigRows, PRUint32(aStartRowIndex));
2277 // rowX keeps track of where we are in mRows while setting up the
2278 // new cellmap.
2279 PRUint32 rowX = 0;
2281 // put back the rows before the affected ones just as before. Note that we
2282 // can't just copy the old rows in bit-for-bit, because they might be
2283 // spanning out into the rows we're adding/removing.
2284 for ( ; rowX < copyEndRowIndex; rowX++) {
2285 const CellDataArray& row = origRows[rowX];
2286 PRUint32 numCols = row.Length();
2287 for (PRUint32 colX = 0; colX < numCols; colX++) {
2288 // put in the original cell from the cell map
2289 const CellData* data = row.ElementAt(colX);
2290 if (data && data->IsOrig()) {
2291 AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea);
2296 // Now handle the new rows being inserted, if any.
2297 PRUint32 copyStartRowIndex;
2298 rowX = aStartRowIndex;
2299 if (aRowsToInsert) {
2300 // add in the new cells and create rows if necessary
2301 PRInt32 numNewRows = aRowsToInsert->Length();
2302 for (PRInt32 newRowX = 0; newRowX < numNewRows; newRowX++) {
2303 nsTableRowFrame* rFrame = aRowsToInsert->ElementAt(newRowX);
2304 nsIFrame* cFrame = rFrame->GetFirstChild(nsnull);
2305 while (cFrame) {
2306 nsTableCellFrame *cellFrame = do_QueryFrame(cFrame);
2307 if (cellFrame) {
2308 AppendCell(aMap, cellFrame, rowX, PR_FALSE, aDamageArea);
2310 cFrame = cFrame->GetNextSibling();
2312 rowX++;
2314 copyStartRowIndex = aStartRowIndex;
2316 else {
2317 copyStartRowIndex = aStartRowIndex + aNumRowsToRemove;
2320 // put back the rows after the affected ones just as before. Again, we can't
2321 // just copy the old bits because that would not handle the new rows spanning
2322 // out or our earlier old rows spanning through the damaged area.
2323 for (PRUint32 copyRowX = copyStartRowIndex; copyRowX < numOrigRows;
2324 copyRowX++) {
2325 const CellDataArray& row = origRows[copyRowX];
2326 PRUint32 numCols = row.Length();
2327 for (PRUint32 colX = 0; colX < numCols; colX++) {
2328 // put in the original cell from the cell map
2329 CellData* data = row.ElementAt(colX);
2330 if (data && data->IsOrig()) {
2331 AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea);
2334 rowX++;
2337 // delete the old cell map. Now rowX no longer has anything to do with mRows
2338 for (rowX = 0; rowX < numOrigRows; rowX++) {
2339 CellDataArray& row = origRows[rowX];
2340 PRUint32 len = row.Length();
2341 for (PRUint32 colX = 0; colX < len; colX++) {
2342 DestroyCellData(row[colX]);
2346 SetDamageArea(0, 0, aMap.GetColCount(), GetRowCount(), aDamageArea);
2349 void
2350 nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap,
2351 PRInt32 aNumOrigCols,
2352 nsTArray<nsTableCellFrame*>* aCellFrames,
2353 PRInt32 aRowIndex,
2354 PRInt32 aColIndex,
2355 PRBool aInsert,
2356 nsRect& aDamageArea)
2358 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2359 // copy the old cell map into a new array
2360 PRInt32 numOrigRows = mRows.Length();
2361 nsTArray<CellDataArray> origRows;
2362 mRows.SwapElements(origRows);
2364 PRInt32 numNewCells = (aCellFrames) ? aCellFrames->Length() : 0;
2366 // the new cells might extend the previous column number
2367 NS_ASSERTION(aNumOrigCols >= aColIndex, "Appending cells far beyond cellmap data?!");
2368 PRInt32 numCols = aInsert ? NS_MAX(aNumOrigCols, aColIndex + 1) : aNumOrigCols;
2370 // build the new cell map. Hard to say what, if anything, we can preallocate
2371 // here... Should come back to that sometime, perhaps.
2372 PRInt32 rowX;
2373 for (rowX = 0; rowX < numOrigRows; rowX++) {
2374 const CellDataArray& row = origRows[rowX];
2375 for (PRInt32 colX = 0; colX < numCols; colX++) {
2376 if ((rowX == aRowIndex) && (colX == aColIndex)) {
2377 if (aInsert) { // put in the new cells
2378 for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) {
2379 nsTableCellFrame* cell = aCellFrames->ElementAt(cellX);
2380 if (cell) {
2381 AppendCell(aMap, cell, rowX, PR_FALSE, aDamageArea);
2385 else {
2386 continue; // do not put the deleted cell back
2389 // put in the original cell from the cell map
2390 CellData* data = row.SafeElementAt(colX);
2391 if (data && data->IsOrig()) {
2392 AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea);
2396 if (aInsert && numOrigRows <= aRowIndex) { // append the new cells below the last original row
2397 NS_ASSERTION (numOrigRows == aRowIndex, "Appending cells far beyond the last row");
2398 for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) {
2399 nsTableCellFrame* cell = aCellFrames->ElementAt(cellX);
2400 if (cell) {
2401 AppendCell(aMap, cell, aRowIndex, PR_FALSE, aDamageArea);
2406 // delete the old cell map
2407 for (rowX = 0; rowX < numOrigRows; rowX++) {
2408 CellDataArray& row = origRows[rowX];
2409 PRUint32 len = row.Length();
2410 for (PRUint32 colX = 0; colX < len; colX++) {
2411 DestroyCellData(row.SafeElementAt(colX));
2414 // expand the cellmap to cover empty content rows
2415 if (mRows.Length() < PRUint32(mContentRowCount)) {
2416 Grow(aMap, mContentRowCount - mRows.Length());
2421 void nsCellMap::RemoveCell(nsTableCellMap& aMap,
2422 nsTableCellFrame* aCellFrame,
2423 PRInt32 aRowIndex,
2424 nsRect& aDamageArea)
2426 PRUint32 numRows = mRows.Length();
2427 if (PRUint32(aRowIndex) >= numRows) {
2428 NS_ERROR("bad arg in nsCellMap::RemoveCell");
2429 return;
2431 PRInt32 numCols = aMap.GetColCount();
2433 // Now aRowIndex is guaranteed OK.
2435 // get the starting col index of the cell to remove
2436 PRInt32 startColIndex;
2437 for (startColIndex = 0; startColIndex < numCols; startColIndex++) {
2438 CellData* data = mRows[aRowIndex].SafeElementAt(startColIndex);
2439 if (data && (data->IsOrig()) && (aCellFrame == data->GetCellFrame())) {
2440 break; // we found the col index
2444 PRInt32 rowSpan = GetRowSpan(aRowIndex, startColIndex, PR_FALSE);
2445 // record whether removing the cells is going to cause complications due
2446 // to existing row spans, col spans or table sizing.
2447 PRBool spansCauseRebuild = CellsSpanInOrOut(aRowIndex,
2448 aRowIndex + rowSpan - 1,
2449 startColIndex, numCols - 1);
2450 // XXX if the cell has a col span to the end of the map, and the end has no originating
2451 // cells, we need to assume that this the only such cell, and rebuild so that there are
2452 // no extraneous cols at the end. The same is true for removing rows.
2453 if (!aCellFrame->GetRowSpan() || !aCellFrame->GetColSpan())
2454 spansCauseRebuild = PR_TRUE;
2456 if (spansCauseRebuild) {
2457 aMap.RebuildConsideringCells(this, nsnull, aRowIndex, startColIndex, PR_FALSE, aDamageArea);
2459 else {
2460 ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex, aDamageArea);
2464 void nsCellMap::ExpandZeroColSpans(nsTableCellMap& aMap)
2466 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2467 PRUint32 numRows = mRows.Length();
2468 PRUint32 numCols = aMap.GetColCount();
2469 PRUint32 rowIndex, colIndex;
2471 for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
2472 for (colIndex = 0; colIndex < numCols; colIndex++) {
2473 CellData* data = mRows[rowIndex].SafeElementAt(colIndex);
2474 if (!data || !data->IsOrig())
2475 continue;
2476 nsTableCellFrame* cell = data->GetCellFrame();
2477 NS_ASSERTION(cell, "There has to be a cell");
2478 PRInt32 cellRowSpan = cell->GetRowSpan();
2479 PRInt32 cellColSpan = cell->GetColSpan();
2480 PRBool rowZeroSpan = (0 == cell->GetRowSpan());
2481 PRBool colZeroSpan = (0 == cell->GetColSpan());
2482 if (colZeroSpan) {
2483 aMap.mTableFrame.SetHasZeroColSpans(PR_TRUE);
2484 // do the expansion
2485 NS_ASSERTION(numRows > 0, "Bogus numRows");
2486 NS_ASSERTION(numCols > 0, "Bogus numCols");
2487 PRUint32 endRowIndex = rowZeroSpan ? numRows - 1 :
2488 rowIndex + cellRowSpan - 1;
2489 PRUint32 endColIndex = colZeroSpan ? numCols - 1 :
2490 colIndex + cellColSpan - 1;
2491 PRUint32 colX, rowX;
2492 colX = colIndex + 1;
2493 while (colX <= endColIndex) {
2494 // look at columns from here to our colspan. For each one, check
2495 // the rows from here to our rowspan to make sure there is no
2496 // obstacle to marking that column as a zerospanned column; if there
2497 // isn't, mark it so
2498 for (rowX = rowIndex; rowX <= endRowIndex; rowX++) {
2499 CellData* oldData = GetDataAt(rowX, colX);
2500 if (oldData) {
2501 if (oldData->IsOrig()) {
2502 break; // something is in the way
2504 if (oldData->IsRowSpan()) {
2505 if ((rowX - rowIndex) != oldData->GetRowSpanOffset()) {
2506 break;
2509 if (oldData->IsColSpan()) {
2510 if ((colX - colIndex) != oldData->GetColSpanOffset()) {
2511 break;
2516 if (endRowIndex >= rowX)
2517 break;// we hit something
2518 for (rowX = rowIndex; rowX <= endRowIndex; rowX++) {
2519 CellData* newData = AllocCellData(nsnull);
2520 if (!newData) return;
2522 newData->SetColSpanOffset(colX - colIndex);
2523 newData->SetZeroColSpan(PR_TRUE);
2525 if (rowX > rowIndex) {
2526 newData->SetRowSpanOffset(rowX - rowIndex);
2527 if (rowZeroSpan)
2528 newData->SetZeroRowSpan(PR_TRUE);
2530 SetDataAt(aMap, *newData, rowX, colX);
2532 colX++;
2533 } // while (colX <= endColIndex)
2534 } // if zerocolspan
2538 #ifdef NS_DEBUG
2539 void nsCellMap::Dump(PRBool aIsBorderCollapse) const
2541 printf("\n ***** START GROUP CELL MAP DUMP ***** %p\n", (void*)this);
2542 nsTableRowGroupFrame* rg = GetRowGroup();
2543 const nsStyleDisplay* display = rg->GetStyleDisplay();
2544 switch (display->mDisplay) {
2545 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
2546 printf(" thead ");
2547 break;
2548 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
2549 printf(" tfoot ");
2550 break;
2551 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
2552 printf(" tbody ");
2553 break;
2554 default:
2555 printf("HUH? wrong display type on rowgroup");
2557 PRUint32 mapRowCount = mRows.Length();
2558 printf("mapRowCount=%u tableRowCount=%d\n", mapRowCount, mContentRowCount);
2561 PRUint32 rowIndex, colIndex;
2562 for (rowIndex = 0; rowIndex < mapRowCount; rowIndex++) {
2563 const CellDataArray& row = mRows[rowIndex];
2564 printf(" row %d : ", rowIndex);
2565 PRUint32 colCount = row.Length();
2566 for (colIndex = 0; colIndex < colCount; colIndex++) {
2567 CellData* cd = row[colIndex];
2568 if (cd) {
2569 if (cd->IsOrig()) {
2570 printf("C%d,%d ", rowIndex, colIndex);
2571 } else {
2572 nsTableCellFrame* cell = nsnull;
2573 if (cd->IsRowSpan()) {
2574 cell = GetCellFrame(rowIndex, colIndex, *cd, PR_TRUE);
2575 printf("R ");
2577 if (cd->IsColSpan()) {
2578 cell = GetCellFrame(rowIndex, colIndex, *cd, PR_FALSE);
2579 printf("C ");
2581 if (!(cd->IsRowSpan() && cd->IsColSpan())) {
2582 printf(" ");
2584 printf(" ");
2586 } else {
2587 printf("---- ");
2590 if (aIsBorderCollapse) {
2591 nscoord size;
2592 BCBorderOwner owner;
2593 PRUint8 side;
2594 PRBool segStart;
2595 PRPackedBool bevel;
2596 for (PRInt32 i = 0; i <= 2; i++) {
2597 printf("\n ");
2598 for (colIndex = 0; colIndex < colCount; colIndex++) {
2599 BCCellData* cd = (BCCellData *)row[colIndex];
2600 if (cd) {
2601 if (0 == i) {
2602 size = cd->mData.GetTopEdge(owner, segStart);
2603 printf("t=%d%d%d ", size, owner, segStart);
2605 else if (1 == i) {
2606 size = cd->mData.GetLeftEdge(owner, segStart);
2607 printf("l=%d%d%d ", size, owner, segStart);
2609 else {
2610 size = cd->mData.GetCorner(side, bevel);
2611 printf("c=%d%d%d ", size, side, bevel);
2617 printf("\n");
2620 // output info mapping Ci,j to cell address
2621 PRUint32 cellCount = 0;
2622 for (PRUint32 rIndex = 0; rIndex < mapRowCount; rIndex++) {
2623 const CellDataArray& row = mRows[rIndex];
2624 PRUint32 colCount = row.Length();
2625 printf(" ");
2626 for (colIndex = 0; colIndex < colCount; colIndex++) {
2627 CellData* cd = row[colIndex];
2628 if (cd) {
2629 if (cd->IsOrig()) {
2630 nsTableCellFrame* cellFrame = cd->GetCellFrame();
2631 PRInt32 cellFrameColIndex;
2632 cellFrame->GetColIndex(cellFrameColIndex);
2633 printf("C%d,%d=%p(%d) ", rIndex, colIndex, (void*)cellFrame,
2634 cellFrameColIndex);
2635 cellCount++;
2639 printf("\n");
2642 printf(" ***** END GROUP CELL MAP DUMP *****\n");
2644 #endif
2646 PRBool
2647 nsCellMap::IsZeroColSpan(PRInt32 aRowIndex,
2648 PRInt32 aColIndex) const
2650 CellData* data =
2651 mRows.SafeElementAt(aRowIndex, *sEmptyRow).SafeElementAt(aColIndex);
2652 return data && data->IsZeroColSpan();
2655 CellData*
2656 nsCellMap::GetDataAt(PRInt32 aMapRowIndex,
2657 PRInt32 aColIndex) const
2659 return
2660 mRows.SafeElementAt(aMapRowIndex, *sEmptyRow).SafeElementAt(aColIndex);
2663 // only called if the cell at aMapRowIndex, aColIndex is null or dead
2664 // (the latter from ExpandZeroColSpans).
2665 void nsCellMap::SetDataAt(nsTableCellMap& aMap,
2666 CellData& aNewCell,
2667 PRInt32 aMapRowIndex,
2668 PRInt32 aColIndex)
2670 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2671 if (PRUint32(aMapRowIndex) >= mRows.Length()) {
2672 NS_ERROR("SetDataAt called with row index > num rows");
2673 return;
2676 CellDataArray& row = mRows[aMapRowIndex];
2678 // the table map may need cols added
2679 PRInt32 numColsToAdd = aColIndex + 1 - aMap.GetColCount();
2680 if (numColsToAdd > 0) {
2681 aMap.AddColsAtEnd(numColsToAdd);
2683 // the row may need cols added
2684 numColsToAdd = aColIndex + 1 - row.Length();
2685 if (numColsToAdd > 0) {
2686 // XXXbz need to handle allocation failures.
2687 GrowRow(row, numColsToAdd);
2690 DestroyCellData(row[aColIndex]);
2692 row.ReplaceElementsAt(aColIndex, 1, &aNewCell);
2693 // update the originating cell counts if cell originates in this row, col
2694 nsColInfo* colInfo = aMap.GetColInfoAt(aColIndex);
2695 if (colInfo) {
2696 if (aNewCell.IsOrig()) {
2697 colInfo->mNumCellsOrig++;
2699 else if (aNewCell.IsColSpan()) {
2700 colInfo->mNumCellsSpan++;
2703 else NS_ERROR("SetDataAt called with col index > table map num cols");
2706 nsTableCellFrame*
2707 nsCellMap::GetCellInfoAt(const nsTableCellMap& aMap,
2708 PRInt32 aRowX,
2709 PRInt32 aColX,
2710 PRBool* aOriginates,
2711 PRInt32* aColSpan) const
2713 if (aOriginates) {
2714 *aOriginates = PR_FALSE;
2716 CellData* data = GetDataAt(aRowX, aColX);
2717 nsTableCellFrame* cellFrame = nsnull;
2718 if (data) {
2719 if (data->IsOrig()) {
2720 cellFrame = data->GetCellFrame();
2721 if (aOriginates)
2722 *aOriginates = PR_TRUE;
2724 else {
2725 cellFrame = GetCellFrame(aRowX, aColX, *data, PR_TRUE);
2727 if (cellFrame && aColSpan) {
2728 PRInt32 initialColIndex;
2729 cellFrame->GetColIndex(initialColIndex);
2730 PRBool zeroSpan;
2731 *aColSpan = GetEffectiveColSpan(aMap, aRowX, initialColIndex, zeroSpan);
2734 return cellFrame;
2738 PRBool nsCellMap::RowIsSpannedInto(PRInt32 aRowIndex,
2739 PRInt32 aNumEffCols) const
2741 if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) {
2742 return PR_FALSE;
2744 for (PRInt32 colIndex = 0; colIndex < aNumEffCols; colIndex++) {
2745 CellData* cd = GetDataAt(aRowIndex, colIndex);
2746 if (cd) { // there's really a cell at (aRowIndex, colIndex)
2747 if (cd->IsSpan()) { // the cell at (aRowIndex, colIndex) is the result of a span
2748 if (cd->IsRowSpan() && GetCellFrame(aRowIndex, colIndex, *cd, PR_TRUE)) { // XXX why the last check
2749 return PR_TRUE;
2754 return PR_FALSE;
2757 PRBool nsCellMap::RowHasSpanningCells(PRInt32 aRowIndex,
2758 PRInt32 aNumEffCols) const
2760 if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) {
2761 return PR_FALSE;
2763 if (aRowIndex != mContentRowCount - 1) {
2764 // aRowIndex is not the last row, so we check the next row after aRowIndex for spanners
2765 for (PRInt32 colIndex = 0; colIndex < aNumEffCols; colIndex++) {
2766 CellData* cd = GetDataAt(aRowIndex, colIndex);
2767 if (cd && (cd->IsOrig())) { // cell originates
2768 CellData* cd2 = GetDataAt(aRowIndex + 1, colIndex);
2769 if (cd2 && cd2->IsRowSpan()) { // cd2 is spanned by a row
2770 if (cd->GetCellFrame() == GetCellFrame(aRowIndex + 1, colIndex, *cd2, PR_TRUE)) {
2771 return PR_TRUE;
2777 return PR_FALSE;
2780 PRBool nsCellMap::ColHasSpanningCells(PRInt32 aColIndex) const
2782 for (PRInt32 rowIndex = 0; rowIndex < mContentRowCount; rowIndex++) {
2783 CellData* cd = GetDataAt(rowIndex, aColIndex);
2784 if (cd && (cd->IsOrig())) { // cell originates
2785 CellData* cd2 = GetDataAt(rowIndex, aColIndex +1);
2786 if (cd2 && cd2->IsColSpan()) { // cd2 is spanned by a col
2787 if (cd->GetCellFrame() == GetCellFrame(rowIndex , aColIndex + 1, *cd2, PR_FALSE)) {
2788 return PR_TRUE;
2793 return PR_FALSE;
2796 void nsCellMap::DestroyCellData(CellData* aData)
2798 if (!aData) {
2799 return;
2802 if (mIsBC) {
2803 BCCellData* bcData = static_cast<BCCellData*>(aData);
2804 bcData->~BCCellData();
2805 mPresContext->FreeToShell(sizeof(BCCellData), bcData);
2806 } else {
2807 aData->~CellData();
2808 mPresContext->FreeToShell(sizeof(CellData), aData);
2812 CellData* nsCellMap::AllocCellData(nsTableCellFrame* aOrigCell)
2814 if (mIsBC) {
2815 BCCellData* data = (BCCellData*)
2816 mPresContext->AllocateFromShell(sizeof(BCCellData));
2817 if (data) {
2818 new (data) BCCellData(aOrigCell);
2820 return data;
2823 CellData* data = (CellData*)
2824 mPresContext->AllocateFromShell(sizeof(CellData));
2825 if (data) {
2826 new (data) CellData(aOrigCell);
2828 return data;
2831 void
2832 nsCellMapColumnIterator::AdvanceRowGroup()
2834 do {
2835 mCurMapStart += mCurMapContentRowCount;
2836 mCurMap = mCurMap->GetNextSibling();
2837 if (!mCurMap) {
2838 // Set mCurMapContentRowCount and mCurMapRelevantRowCount to 0 in case
2839 // mCurMap has no next sibling. This can happen if we just handled the
2840 // last originating cell. Future calls will end up with mFoundCells ==
2841 // mOrigCells, but for this one mFoundCells was definitely not big enough
2842 // if we got here.
2843 mCurMapContentRowCount = 0;
2844 mCurMapRelevantRowCount = 0;
2845 break;
2848 mCurMapContentRowCount = mCurMap->GetRowCount();
2849 PRUint32 rowArrayLength = mCurMap->mRows.Length();
2850 mCurMapRelevantRowCount = NS_MIN(mCurMapContentRowCount, rowArrayLength);
2851 } while (0 == mCurMapRelevantRowCount);
2853 NS_ASSERTION(mCurMapRelevantRowCount != 0 || !mCurMap,
2854 "How did that happen?");
2856 // Set mCurMapRow to 0, since cells can't span across table row groups.
2857 mCurMapRow = 0;
2860 void
2861 nsCellMapColumnIterator::IncrementRow(PRInt32 aIncrement)
2863 NS_PRECONDITION(aIncrement >= 0, "Bogus increment");
2864 NS_PRECONDITION(mCurMap, "Bogus mOrigCells?");
2865 if (aIncrement == 0) {
2866 AdvanceRowGroup();
2868 else {
2869 mCurMapRow += aIncrement;
2870 if (mCurMapRow >= mCurMapRelevantRowCount) {
2871 AdvanceRowGroup();
2876 nsTableCellFrame*
2877 nsCellMapColumnIterator::GetNextFrame(PRInt32* aRow, PRInt32* aColSpan)
2879 // Fast-path for the case when we don't have anything left in the column and
2880 // we know it.
2881 if (mFoundCells == mOrigCells) {
2882 *aRow = 0;
2883 *aColSpan = 1;
2884 return nsnull;
2887 while (1) {
2888 NS_ASSERTION(mCurMapRow < mCurMapRelevantRowCount, "Bogus mOrigCells?");
2889 // Safe to just get the row (which is faster than calling GetDataAt(), but
2890 // there may not be that many cells in it, so have to use SafeElementAt for
2891 // the mCol.
2892 const nsCellMap::CellDataArray& row = mCurMap->mRows[mCurMapRow];
2893 CellData* cellData = row.SafeElementAt(mCol);
2894 if (!cellData || cellData->IsDead()) {
2895 // Could hit this if there are fewer cells in this row than others, for
2896 // example.
2897 IncrementRow(1);
2898 continue;
2901 if (cellData->IsColSpan()) {
2902 // Look up the originating data for this cell, advance by its relative rowspan.
2903 PRInt32 rowspanOffset = cellData->GetRowSpanOffset();
2904 nsTableCellFrame* cellFrame = mCurMap->GetCellFrame(mCurMapRow, mCol, *cellData, PR_FALSE);
2905 NS_ASSERTION(cellFrame,"Must have usable originating data here");
2906 PRInt32 rowSpan = cellFrame->GetRowSpan();
2907 if (rowSpan == 0) {
2908 AdvanceRowGroup();
2910 else {
2911 IncrementRow(rowSpan - rowspanOffset);
2913 continue;
2916 NS_ASSERTION(cellData->IsOrig(),
2917 "Must have originating cellData by this point. "
2918 "See comment on mCurMapRow in header.");
2920 nsTableCellFrame* cellFrame = cellData->GetCellFrame();
2921 NS_ASSERTION(cellFrame, "Orig data without cellframe?");
2923 *aRow = mCurMapStart + mCurMapRow;
2924 PRBool ignoredZeroSpan;
2925 *aColSpan = mCurMap->GetEffectiveColSpan(*mMap, mCurMapRow, mCol,
2926 ignoredZeroSpan);
2928 IncrementRow(cellFrame->GetRowSpan());
2930 ++mFoundCells;
2932 NS_ASSERTION(cellData = mMap->GetDataAt(*aRow, mCol),
2933 "Giving caller bogus row?");
2935 return cellFrame;
2938 NS_NOTREACHED("Can't get here");
2939 return nsnull;