Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / tables / nsCellMap.cpp
blobf88daf100dc40026b8ca642f401826d7b7246448
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 "nsVoidArray.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 PRInt32 colCount = mCols.Count();
105 for (PRInt32 colX = 0; colX < colCount; colX++) {
106 nsColInfo* colInfo = (nsColInfo *)mCols.ElementAt(colX);
107 if (colInfo) {
108 delete colInfo;
111 if (mBCInfo) {
112 DeleteRightBottomBorders();
113 delete mBCInfo;
117 // Get the bcData holding the border segments of the right edge of the table
118 BCData*
119 nsTableCellMap::GetRightMostBorder(PRInt32 aRowIndex)
121 if (!mBCInfo) ABORT1(nsnull);
123 PRInt32 numRows = mBCInfo->mRightBorders.Count();
124 if (aRowIndex < numRows) {
125 return (BCData*)mBCInfo->mRightBorders.ElementAt(aRowIndex);
128 BCData* bcData;
129 PRInt32 rowX = numRows;
131 do {
132 bcData = new BCData();
133 if (!bcData) ABORT1(nsnull);
134 mBCInfo->mRightBorders.AppendElement(bcData);
135 } while (++rowX <= aRowIndex);
137 return bcData;
140 // Get the bcData holding the border segments of the bottom edge of the table
141 BCData*
142 nsTableCellMap::GetBottomMostBorder(PRInt32 aColIndex)
144 if (!mBCInfo) ABORT1(nsnull);
146 PRInt32 numCols = mBCInfo->mBottomBorders.Count();
147 if (aColIndex < numCols) {
148 return (BCData*)mBCInfo->mBottomBorders.ElementAt(aColIndex);
151 BCData* bcData;
152 PRInt32 colX = numCols;
154 do {
155 bcData = new BCData();
156 if (!bcData) ABORT1(nsnull);
157 mBCInfo->mBottomBorders.AppendElement(bcData);
158 } while (++colX <= aColIndex);
160 return bcData;
163 // delete the borders corresponding to the right and bottom edges of the table
164 void
165 nsTableCellMap::DeleteRightBottomBorders()
167 if (mBCInfo) {
168 PRInt32 numCols = mBCInfo->mBottomBorders.Count();
169 if (numCols > 0) {
170 for (PRInt32 colX = numCols - 1; colX >= 0; colX--) {
171 BCData* bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(colX);
172 if (bcData) {
173 delete bcData;
175 mBCInfo->mBottomBorders.RemoveElementAt(colX);
178 PRUint32 numRows = mBCInfo->mRightBorders.Count();
179 if (numRows > 0) {
180 for (PRInt32 rowX = numRows - 1; rowX >= 0; rowX--) {
181 BCData* bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(rowX);
182 if (bcData) {
183 delete bcData;
185 mBCInfo->mRightBorders.RemoveElementAt(rowX);
191 void
192 nsTableCellMap::InsertGroupCellMap(nsCellMap* aPrevMap,
193 nsCellMap& aNewMap)
195 nsCellMap* next;
196 if (aPrevMap) {
197 next = aPrevMap->GetNextSibling();
198 aPrevMap->SetNextSibling(&aNewMap);
200 else {
201 next = mFirstMap;
202 mFirstMap = &aNewMap;
204 aNewMap.SetNextSibling(next);
207 void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame& aNewGroup,
208 nsTableRowGroupFrame*& aPrevGroup)
210 nsCellMap* newMap = new nsCellMap(aNewGroup, mBCInfo != nsnull);
211 if (newMap) {
212 nsCellMap* prevMap = nsnull;
213 nsCellMap* lastMap = mFirstMap;
214 if (aPrevGroup) {
215 nsCellMap* map = mFirstMap;
216 while (map) {
217 lastMap = map;
218 if (map->GetRowGroup() == aPrevGroup) {
219 prevMap = map;
220 break;
222 map = map->GetNextSibling();
225 if (!prevMap) {
226 if (aPrevGroup) {
227 prevMap = lastMap;
228 aPrevGroup = (prevMap) ? prevMap->GetRowGroup() : nsnull;
230 else {
231 aPrevGroup = nsnull;
234 InsertGroupCellMap(prevMap, *newMap);
238 void nsTableCellMap::RemoveGroupCellMap(nsTableRowGroupFrame* aGroup)
240 nsCellMap* map = mFirstMap;
241 nsCellMap* prior = nsnull;
242 while (map) {
243 if (map->GetRowGroup() == aGroup) {
244 nsCellMap* next = map->GetNextSibling();
245 if (mFirstMap == map) {
246 mFirstMap = next;
248 else {
249 prior->SetNextSibling(next);
251 delete map;
252 break;
254 prior = map;
255 map = map->GetNextSibling();
259 static nsCellMap*
260 FindMapFor(const nsTableRowGroupFrame* aRowGroup,
261 nsCellMap* aStart,
262 const nsCellMap* aEnd)
264 for (nsCellMap* map = aStart; map != aEnd; map = map->GetNextSibling()) {
265 if (aRowGroup == map->GetRowGroup()) {
266 return map;
270 return nsnull;
273 nsCellMap*
274 nsTableCellMap::GetMapFor(const nsTableRowGroupFrame* aRowGroup,
275 nsCellMap* aStartHint) const
277 NS_PRECONDITION(aRowGroup, "Must have a rowgroup");
278 NS_ASSERTION(!aRowGroup->GetPrevInFlow(), "GetMapFor called with continuation");
279 if (aStartHint) {
280 nsCellMap* map = FindMapFor(aRowGroup, aStartHint, nsnull);
281 if (map) {
282 return map;
286 nsCellMap* map = FindMapFor(aRowGroup, mFirstMap, aStartHint);
287 if (map) {
288 return map;
291 // if aRowGroup is a repeated header or footer find the header or footer it was repeated from
292 if (aRowGroup->IsRepeatable()) {
293 nsTableFrame* fifTable = static_cast<nsTableFrame*>(mTableFrame.GetFirstInFlow());
295 const nsStyleDisplay* display = aRowGroup->GetStyleDisplay();
296 nsTableRowGroupFrame* rgOrig =
297 (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay) ?
298 fifTable->GetTHead() : fifTable->GetTFoot();
299 // find the row group cell map using the original header/footer
300 if (rgOrig && rgOrig != aRowGroup) {
301 return GetMapFor(rgOrig, aStartHint);
305 return nsnull;
308 void
309 nsTableCellMap::Synchronize(nsTableFrame* aTableFrame)
311 nsTableFrame::RowGroupArray orderedRowGroups;
312 nsAutoTPtrArray<nsCellMap, 8> maps;
314 aTableFrame->OrderRowGroups(orderedRowGroups);
315 if (!orderedRowGroups.Length()) {
316 return;
319 // XXXbz this fails if orderedRowGroups is missing some row groups
320 // (due to OOM when appending to the array, e.g. -- we leak maps in
321 // that case).
323 // Scope |map| outside the loop so we can use it as a hint.
324 nsCellMap* map = nsnull;
325 for (PRUint32 rgX = 0; rgX < orderedRowGroups.Length(); rgX++) {
326 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX];
327 map = GetMapFor((nsTableRowGroupFrame*)rgFrame->GetFirstInFlow(), map);
328 if (map) {
329 if (!maps.AppendElement(map)) {
330 delete map;
331 NS_WARNING("Could not AppendElement");
336 PRInt32 mapIndex = maps.Length() - 1; // Might end up -1
337 nsCellMap* nextMap = maps.ElementAt(mapIndex);
338 nextMap->SetNextSibling(nsnull);
339 for (mapIndex-- ; mapIndex >= 0; mapIndex--) {
340 nsCellMap* map = maps.ElementAt(mapIndex);
341 map->SetNextSibling(nextMap);
342 nextMap = map;
344 mFirstMap = nextMap;
347 PRBool
348 nsTableCellMap::HasMoreThanOneCell(PRInt32 aRowIndex) const
350 PRInt32 rowIndex = aRowIndex;
351 nsCellMap* map = mFirstMap;
352 while (map) {
353 if (map->GetRowCount() > rowIndex) {
354 return map->HasMoreThanOneCell(rowIndex);
356 rowIndex -= map->GetRowCount();
357 map = map->GetNextSibling();
359 return PR_FALSE;
362 PRInt32
363 nsTableCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const
365 PRInt32 rowIndex = aRowIndex;
366 nsCellMap* map = mFirstMap;
367 while (map) {
368 if (map->GetRowCount() > rowIndex) {
369 return map->GetNumCellsOriginatingInRow(rowIndex);
371 rowIndex -= map->GetRowCount();
372 map = map->GetNextSibling();
374 return 0;
376 PRInt32
377 nsTableCellMap::GetEffectiveRowSpan(PRInt32 aRowIndex,
378 PRInt32 aColIndex) const
380 PRInt32 rowIndex = aRowIndex;
381 nsCellMap* map = mFirstMap;
382 while (map) {
383 if (map->GetRowCount() > rowIndex) {
384 return map->GetRowSpan(rowIndex, aColIndex, PR_TRUE);
386 rowIndex -= map->GetRowCount();
387 map = map->GetNextSibling();
389 NS_NOTREACHED("Bogus row index?");
390 return 0;
393 PRInt32
394 nsTableCellMap::GetEffectiveColSpan(PRInt32 aRowIndex,
395 PRInt32 aColIndex) const
397 PRInt32 rowIndex = aRowIndex;
398 nsCellMap* map = mFirstMap;
399 while (map) {
400 if (map->GetRowCount() > rowIndex) {
401 PRBool zeroColSpan;
402 return map->GetEffectiveColSpan(*this, rowIndex, aColIndex, zeroColSpan);
404 rowIndex -= map->GetRowCount();
405 map = map->GetNextSibling();
407 NS_NOTREACHED("Bogus row index?");
408 return 0;
411 nsTableCellFrame*
412 nsTableCellMap::GetCellFrame(PRInt32 aRowIndex,
413 PRInt32 aColIndex,
414 CellData& aData,
415 PRBool aUseRowIfOverlap) const
417 PRInt32 rowIndex = aRowIndex;
418 nsCellMap* map = mFirstMap;
419 while (map) {
420 if (map->GetRowCount() > rowIndex) {
421 return map->GetCellFrame(rowIndex, aColIndex, aData, aUseRowIfOverlap);
423 rowIndex -= map->GetRowCount();
424 map = map->GetNextSibling();
426 return nsnull;
429 nsColInfo*
430 nsTableCellMap::GetColInfoAt(PRInt32 aColIndex)
432 PRInt32 numColsToAdd = aColIndex + 1 - mCols.Count();
433 if (numColsToAdd > 0) {
434 AddColsAtEnd(numColsToAdd); // XXX this could fail to add cols in theory
436 return (nsColInfo*)mCols.ElementAt(aColIndex);
439 PRInt32
440 nsTableCellMap::GetRowCount() const
442 PRInt32 numRows = 0;
443 nsCellMap* map = mFirstMap;
444 while (map) {
445 numRows += map->GetRowCount();
446 map = map->GetNextSibling();
448 return numRows;
451 CellData*
452 nsTableCellMap::GetDataAt(PRInt32 aRowIndex,
453 PRInt32 aColIndex) const
455 PRInt32 rowIndex = aRowIndex;
456 nsCellMap* map = mFirstMap;
457 while (map) {
458 if (map->GetRowCount() > rowIndex) {
459 return map->GetDataAt(rowIndex, aColIndex);
461 rowIndex -= map->GetRowCount();
462 map = map->GetNextSibling();
464 return nsnull;
467 void
468 nsTableCellMap::AddColsAtEnd(PRUint32 aNumCols)
470 PRBool added;
471 // XXX We really should have a way to say "make this voidarray at least
472 // N entries long" to avoid reallocating N times. On the other hand, the
473 // number of likely allocations here isn't TOO gigantic, and we may not
474 // know about many of them at a time.
475 for (PRUint32 numX = 1; numX <= aNumCols; numX++) {
476 nsColInfo* colInfo = new nsColInfo();
477 if (colInfo) {
478 added = mCols.AppendElement(colInfo);
479 if (!added) {
480 delete colInfo;
481 NS_WARNING("Could not AppendElement");
484 if (mBCInfo) {
485 BCData* bcData = new BCData();
486 if (bcData) {
487 added = mBCInfo->mBottomBorders.AppendElement(bcData);
488 if (!added) {
489 delete bcData;
490 NS_WARNING("Could not AppendElement");
497 void
498 nsTableCellMap::RemoveColsAtEnd()
500 // Remove the cols at the end which don't have originating cells or cells spanning
501 // into them. Only do this if the col was created as eColAnonymousCell
502 PRInt32 numCols = GetColCount();
503 PRInt32 lastGoodColIndex = mTableFrame.GetIndexOfLastRealCol();
504 for (PRInt32 colX = numCols - 1; (colX >= 0) && (colX > lastGoodColIndex); colX--) {
505 nsColInfo* colInfo = (nsColInfo*)mCols.ElementAt(colX);
506 if (colInfo) {
507 if ((colInfo->mNumCellsOrig <= 0) && (colInfo->mNumCellsSpan <= 0)) {
509 delete colInfo;
510 mCols.RemoveElementAt(colX);
512 if (mBCInfo) {
513 PRInt32 count = mBCInfo->mBottomBorders.Count();
514 if (colX < count) {
515 BCData* bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(colX);
516 if (bcData) {
517 delete bcData;
519 mBCInfo->mBottomBorders.RemoveElementAt(colX);
523 else break; // only remove until we encounter the 1st valid one
525 else {
526 NS_ERROR("null entry in column info array");
527 mCols.RemoveElementAt(colX);
532 void
533 nsTableCellMap::ClearCols()
535 PRInt32 numCols = GetColCount();
536 for (PRInt32 colX = numCols - 1; (colX >= 0);colX--) {
537 nsColInfo* colInfo = (nsColInfo*)mCols.ElementAt(colX);
538 delete colInfo;
539 mCols.RemoveElementAt(colX);
540 if (mBCInfo) {
541 PRInt32 count = mBCInfo->mBottomBorders.Count();
542 if (colX < count) {
543 BCData* bcData = (BCData*)mBCInfo->mBottomBorders.ElementAt(colX);
544 if (bcData) {
545 delete bcData;
547 mBCInfo->mBottomBorders.RemoveElementAt(colX);
552 void
553 nsTableCellMap::InsertRows(nsTableRowGroupFrame& aParent,
554 nsVoidArray& aRows,
555 PRInt32 aFirstRowIndex,
556 PRBool aConsiderSpans,
557 nsRect& aDamageArea)
559 PRInt32 numNewRows = aRows.Count();
560 if ((numNewRows <= 0) || (aFirstRowIndex < 0)) ABORT0();
562 PRInt32 rowIndex = aFirstRowIndex;
563 nsCellMap* cellMap = mFirstMap;
564 while (cellMap) {
565 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
566 if (rg == &aParent) {
567 cellMap->InsertRows(*this, aRows, rowIndex, aConsiderSpans, aDamageArea);
568 aDamageArea.y = PR_MIN(aFirstRowIndex, aDamageArea.y);
569 aDamageArea.height = PR_MAX(0, GetRowCount() - aDamageArea.y);
570 #ifdef DEBUG_TABLE_CELLMAP
571 Dump("after InsertRows");
572 #endif
573 if (mBCInfo) {
574 BCData* bcData;
575 PRInt32 count = mBCInfo->mRightBorders.Count();
576 if (aFirstRowIndex < count) {
577 for (PRInt32 rowX = aFirstRowIndex; rowX < aFirstRowIndex + numNewRows; rowX++) {
578 bcData = new BCData(); if (!bcData) ABORT0();
579 mBCInfo->mRightBorders.InsertElementAt(bcData, rowX);
582 else {
583 GetRightMostBorder(aFirstRowIndex); // this will create missing entries
584 for (PRInt32 rowX = aFirstRowIndex + 1; rowX < aFirstRowIndex + numNewRows; rowX++) {
585 bcData = new BCData(); if (!bcData) ABORT0();
586 mBCInfo->mRightBorders.AppendElement(bcData);
590 return;
592 rowIndex -= cellMap->GetRowCount();
593 cellMap = cellMap->GetNextSibling();
596 NS_ERROR("Attempt to insert row into wrong map.");
599 void
600 nsTableCellMap::RemoveRows(PRInt32 aFirstRowIndex,
601 PRInt32 aNumRowsToRemove,
602 PRBool aConsiderSpans,
603 nsRect& aDamageArea)
605 PRInt32 rowIndex = aFirstRowIndex;
606 nsCellMap* cellMap = mFirstMap;
607 while (cellMap) {
608 if (cellMap->GetRowCount() > rowIndex) {
609 cellMap->RemoveRows(*this, rowIndex, aNumRowsToRemove, aConsiderSpans, aDamageArea);
610 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
611 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
612 aDamageArea.height = PR_MAX(0, GetRowCount() - aFirstRowIndex);
613 if (mBCInfo) {
614 BCData* bcData;
615 for (PRInt32 rowX = aFirstRowIndex + aNumRowsToRemove - 1; rowX >= aFirstRowIndex; rowX--) {
616 if (rowX < mBCInfo->mRightBorders.Count()) {
617 bcData = (BCData*)mBCInfo->mRightBorders.ElementAt(rowX);
618 if (bcData) {
619 delete bcData;
621 mBCInfo->mRightBorders.RemoveElementAt(rowX);
625 break;
627 rowIndex -= cellMap->GetRowCount();
628 cellMap = cellMap->GetNextSibling();
630 #ifdef DEBUG_TABLE_CELLMAP
631 Dump("after RemoveRows");
632 #endif
637 CellData*
638 nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame,
639 PRInt32 aRowIndex,
640 PRBool aRebuildIfNecessary,
641 nsRect& aDamageArea)
643 NS_ASSERTION(&aCellFrame == aCellFrame.GetFirstInFlow(), "invalid call on continuing frame");
644 nsIFrame* rgFrame = aCellFrame.GetParent(); // get the row
645 if (!rgFrame) return 0;
646 rgFrame = rgFrame->GetParent(); // get the row group
647 if (!rgFrame) return 0;
649 CellData* result = nsnull;
650 PRInt32 rowIndex = aRowIndex;
651 nsCellMap* cellMap = mFirstMap;
652 while (cellMap) {
653 if (cellMap->GetRowGroup() == rgFrame) {
654 result = cellMap->AppendCell(*this, &aCellFrame, rowIndex, aRebuildIfNecessary, aDamageArea);
655 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
656 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
657 break;
659 rowIndex -= cellMap->GetRowCount();
660 cellMap = cellMap->GetNextSibling();
662 #ifdef DEBUG_TABLE_CELLMAP
663 Dump("after AppendCell");
664 #endif
665 return result;
669 void
670 nsTableCellMap::InsertCells(nsVoidArray& aCellFrames,
671 PRInt32 aRowIndex,
672 PRInt32 aColIndexBefore,
673 nsRect& aDamageArea)
675 PRInt32 rowIndex = aRowIndex;
676 nsCellMap* cellMap = mFirstMap;
677 while (cellMap) {
678 if (cellMap->GetRowCount() > rowIndex) {
679 cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore, aDamageArea);
680 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
681 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
682 aDamageArea.width = PR_MAX(0, GetColCount() - aColIndexBefore - 1);
683 break;
685 rowIndex -= cellMap->GetRowCount();
686 cellMap = cellMap->GetNextSibling();
688 #ifdef DEBUG_TABLE_CELLMAP
689 Dump("after InsertCells");
690 #endif
694 void
695 nsTableCellMap::RemoveCell(nsTableCellFrame* aCellFrame,
696 PRInt32 aRowIndex,
697 nsRect& aDamageArea)
699 if (!aCellFrame) ABORT0();
700 NS_ASSERTION(aCellFrame == (nsTableCellFrame *)aCellFrame->GetFirstInFlow(),
701 "invalid call on continuing frame");
702 PRInt32 rowIndex = aRowIndex;
703 nsCellMap* cellMap = mFirstMap;
704 while (cellMap) {
705 if (cellMap->GetRowCount() > rowIndex) {
706 cellMap->RemoveCell(*this, aCellFrame, rowIndex, aDamageArea);
707 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
708 aDamageArea.y += (rg) ? rg->GetStartRowIndex() : 0;
709 PRInt32 colIndex;
710 aCellFrame->GetColIndex(colIndex);
711 aDamageArea.width = PR_MAX(0, GetColCount() - colIndex - 1);
712 #ifdef DEBUG_TABLE_CELLMAP
713 Dump("after RemoveCell");
714 #endif
715 return;
717 rowIndex -= cellMap->GetRowCount();
718 cellMap = cellMap->GetNextSibling();
720 // if we reach this point - the cell did not get removed, the caller of this routine
721 // will delete the cell and the cellmap will probably hold a reference to
722 // the deleted cell which will cause a subsequent crash when this cell is
723 // referenced later
724 NS_ERROR("nsTableCellMap::RemoveCell - could not remove cell");
727 void
728 SetDamageArea(PRInt32 aXOrigin,
729 PRInt32 aYOrigin,
730 PRInt32 aWidth,
731 PRInt32 aHeight,
732 nsRect& aDamageArea)
734 aDamageArea.x = aXOrigin;
735 aDamageArea.y = aYOrigin;
736 aDamageArea.width = PR_MAX(1, aWidth);
737 aDamageArea.height = PR_MAX(1, aHeight);
740 void
741 nsTableCellMap::RebuildConsideringCells(nsCellMap* aCellMap,
742 nsVoidArray* aCellFrames,
743 PRInt32 aRowIndex,
744 PRInt32 aColIndex,
745 PRBool aInsert,
746 nsRect& aDamageArea)
748 PRInt32 numOrigCols = GetColCount();
749 ClearCols();
750 nsCellMap* cellMap = mFirstMap;
751 PRInt32 rowCount = 0;
752 while (cellMap) {
753 if (cellMap == aCellMap) {
754 cellMap->RebuildConsideringCells(*this, numOrigCols, aCellFrames, aRowIndex, aColIndex, aInsert, aDamageArea);
757 else {
758 cellMap->RebuildConsideringCells(*this, numOrigCols, nsnull, -1, 0, PR_FALSE, aDamageArea);
760 rowCount += cellMap->GetRowCount();
761 cellMap = cellMap->GetNextSibling();
763 SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea);
766 void
767 nsTableCellMap::RebuildConsideringRows(nsCellMap* aCellMap,
768 PRInt32 aStartRowIndex,
769 nsVoidArray* aRowsToInsert,
770 PRInt32 aNumRowsToRemove,
771 nsRect& aDamageArea)
773 NS_PRECONDITION(!aRowsToInsert || aNumRowsToRemove == 0,
774 "Can't handle both removing and inserting rows at once");
776 PRInt32 numOrigCols = GetColCount();
777 ClearCols();
778 nsCellMap* cellMap = mFirstMap;
779 PRInt32 rowCount = 0;
780 while (cellMap) {
781 if (cellMap == aCellMap) {
782 cellMap->RebuildConsideringRows(*this, aStartRowIndex, aRowsToInsert, aNumRowsToRemove, aDamageArea);
784 else {
785 cellMap->RebuildConsideringCells(*this, numOrigCols, nsnull, -1, 0, PR_FALSE, aDamageArea);
787 rowCount += cellMap->GetRowCount();
788 cellMap = cellMap->GetNextSibling();
790 SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea);
793 PRInt32
794 nsTableCellMap::GetNumCellsOriginatingInCol(PRInt32 aColIndex) const
796 PRInt32 colCount = mCols.Count();
797 if ((aColIndex >= 0) && (aColIndex < colCount)) {
798 return ((nsColInfo *)mCols.ElementAt(aColIndex))->mNumCellsOrig;
800 else {
801 NS_ERROR("nsCellMap::GetNumCellsOriginatingInCol - bad col index");
802 return 0;
806 #ifdef NS_DEBUG
807 void
808 nsTableCellMap::Dump(char* aString) const
810 if (aString)
811 printf("%s \n", aString);
812 printf("***** START TABLE CELL MAP DUMP ***** %p\n", (void*)this);
813 // output col info
814 PRInt32 colCount = mCols.Count();
815 printf ("cols array orig/span-> %p", (void*)this);
816 for (PRInt32 colX = 0; colX < colCount; colX++) {
817 nsColInfo* colInfo = (nsColInfo *)mCols.ElementAt(colX);
818 printf ("%d=%d/%d ", colX, colInfo->mNumCellsOrig, colInfo->mNumCellsSpan);
820 printf(" cols in cache %d\n", mTableFrame.GetColCache().Count());
821 nsCellMap* cellMap = mFirstMap;
822 while (cellMap) {
823 cellMap->Dump(nsnull != mBCInfo);
824 cellMap = cellMap->GetNextSibling();
826 if (nsnull != mBCInfo) {
827 printf("***** bottom borders *****\n");
828 nscoord size;
829 BCBorderOwner owner;
830 PRUint8 side;
831 PRBool segStart;
832 PRPackedBool bevel;
833 PRInt32 colIndex;
834 PRInt32 numCols = mBCInfo->mBottomBorders.Count();
835 for (PRInt32 i = 0; i <= 2; i++) {
837 printf("\n ");
838 for (colIndex = 0; colIndex < numCols; colIndex++) {
839 BCData* cd = (BCData*)mBCInfo->mBottomBorders.ElementAt(colIndex);;
840 if (cd) {
841 if (0 == i) {
842 size = cd->GetTopEdge(owner, segStart);
843 printf("t=%d%X%d ", size, owner, segStart);
845 else if (1 == i) {
846 size = cd->GetLeftEdge(owner, segStart);
847 printf("l=%d%X%d ", size, owner, segStart);
849 else {
850 size = cd->GetCorner(side, bevel);
851 printf("c=%d%X%d ", size, side, bevel);
855 BCData* cd = &mBCInfo->mLowerRightCorner;
856 if (cd) {
857 if (0 == i) {
858 size = cd->GetTopEdge(owner, segStart);
859 printf("t=%d%X%d ", size, owner, segStart);
861 else if (1 == i) {
862 size = cd->GetLeftEdge(owner, segStart);
863 printf("l=%d%X%d ", size, owner, segStart);
865 else {
866 size = cd->GetCorner(side, bevel);
867 printf("c=%d%X%d ", size, side, bevel);
871 printf("\n");
873 printf("***** END TABLE CELL MAP DUMP *****\n");
875 #endif
877 nsTableCellFrame*
878 nsTableCellMap::GetCellInfoAt(PRInt32 aRowIndex,
879 PRInt32 aColIndex,
880 PRBool* aOriginates,
881 PRInt32* aColSpan) const
883 PRInt32 rowIndex = aRowIndex;
884 nsCellMap* cellMap = mFirstMap;
885 while (cellMap) {
886 if (cellMap->GetRowCount() > rowIndex) {
887 return cellMap->GetCellInfoAt(*this, rowIndex, aColIndex, aOriginates, aColSpan);
889 rowIndex -= cellMap->GetRowCount();
890 cellMap = cellMap->GetNextSibling();
892 return nsnull;
895 PRInt32
896 nsTableCellMap::GetIndexByRowAndColumn(PRInt32 aRow, PRInt32 aColumn) const
898 PRInt32 index = 0;
900 PRInt32 colCount = mCols.Count();
901 PRInt32 rowIndex = aRow;
903 nsCellMap* cellMap = mFirstMap;
904 while (cellMap) {
905 PRInt32 rowCount = cellMap->GetRowCount();
906 if (rowIndex >= rowCount) {
907 // If the rowCount is less than the rowIndex, this means that the index is
908 // not within the current map. If so, get the index of the last cell in
909 // the last row.
910 PRInt32 cellMapIdx = cellMap->GetIndexByRowAndColumn(colCount,
911 rowCount - 1,
912 colCount - 1);
913 if (cellMapIdx != -1) {
914 index += cellMapIdx + 1;
915 rowIndex -= rowCount;
917 } else {
918 // Index is in valid range for this cellmap, so get the index of rowIndex
919 // and aColumn.
920 PRInt32 cellMapIdx = cellMap->GetIndexByRowAndColumn(colCount, rowIndex,
921 aColumn);
922 if (cellMapIdx != -1) {
923 index += cellMapIdx;
924 return index; // no need to look through further maps here
928 cellMap = cellMap->GetNextSibling();
931 return -1;
934 void
935 nsTableCellMap::GetRowAndColumnByIndex(PRInt32 aIndex,
936 PRInt32 *aRow, PRInt32 *aColumn) const
938 *aRow = -1;
939 *aColumn = -1;
941 PRInt32 colCount = mCols.Count();
943 PRInt32 previousRows = 0;
944 PRInt32 index = aIndex;
946 nsCellMap* cellMap = mFirstMap;
947 while (cellMap) {
948 PRInt32 rowCount = cellMap->GetRowCount();
949 // Determine the highest possible index in this map to see
950 // if wanted index is in here.
951 PRInt32 cellMapIdx = cellMap->GetIndexByRowAndColumn(colCount,
952 rowCount - 1,
953 colCount - 1);
954 if (cellMapIdx != -1) {
955 if (index > cellMapIdx) {
956 // The index is not within this map, so decrease it by the cellMapIdx
957 // determined index and increase the total row index accordingly.
958 index -= cellMapIdx + 1;
959 previousRows += rowCount;
960 } else {
961 cellMap->GetRowAndColumnByIndex(colCount, index, aRow, aColumn);
962 // If there were previous indexes, take them into account.
963 *aRow += previousRows;
964 return; // no need to look any further.
968 cellMap = cellMap->GetNextSibling();
972 PRBool nsTableCellMap::RowIsSpannedInto(PRInt32 aRowIndex,
973 PRInt32 aNumEffCols) const
975 PRInt32 rowIndex = aRowIndex;
976 nsCellMap* cellMap = mFirstMap;
977 while (cellMap) {
978 if (cellMap->GetRowCount() > rowIndex) {
979 return cellMap->RowIsSpannedInto(rowIndex, aNumEffCols);
981 rowIndex -= cellMap->GetRowCount();
982 cellMap = cellMap->GetNextSibling();
984 return PR_FALSE;
987 PRBool nsTableCellMap::RowHasSpanningCells(PRInt32 aRowIndex,
988 PRInt32 aNumEffCols) const
990 PRInt32 rowIndex = aRowIndex;
991 nsCellMap* cellMap = mFirstMap;
992 while (cellMap) {
993 if (cellMap->GetRowCount() > rowIndex) {
994 return cellMap->RowHasSpanningCells(rowIndex, aNumEffCols);
996 rowIndex -= cellMap->GetRowCount();
997 cellMap = cellMap->GetNextSibling();
999 return PR_FALSE;
1002 PRBool nsTableCellMap::ColIsSpannedInto(PRInt32 aColIndex) const
1004 PRBool result = PR_FALSE;
1006 PRInt32 colCount = mCols.Count();
1007 if ((aColIndex >= 0) && (aColIndex < colCount)) {
1008 result = ((nsColInfo *)mCols.ElementAt(aColIndex))->mNumCellsSpan != 0;
1010 return result;
1013 PRBool nsTableCellMap::ColHasSpanningCells(PRInt32 aColIndex) const
1015 NS_PRECONDITION (aColIndex < GetColCount(), "bad col index arg");
1016 nsCellMap* cellMap = mFirstMap;
1017 while (cellMap) {
1018 if (cellMap->ColHasSpanningCells(aColIndex)) {
1019 return PR_TRUE;
1021 cellMap = cellMap->GetNextSibling();
1023 return PR_FALSE;
1026 void nsTableCellMap::ExpandZeroColSpans()
1028 mTableFrame.SetNeedColSpanExpansion(PR_FALSE); // mark the work done
1029 mTableFrame.SetHasZeroColSpans(PR_FALSE); // reset the bit, if there is a
1030 // zerospan it will be set again.
1031 nsCellMap* cellMap = mFirstMap;
1032 while (cellMap) {
1033 cellMap->ExpandZeroColSpans(*this);
1034 cellMap = cellMap->GetNextSibling();
1037 BCData*
1038 nsTableCellMap::GetBCData(PRUint8 aSide,
1039 nsCellMap& aCellMap,
1040 PRUint32 aRowIndex,
1041 PRUint32 aColIndex,
1042 PRBool aIsLowerRight)
1044 if (!mBCInfo || aIsLowerRight) ABORT1(nsnull);
1046 BCCellData* cellData;
1047 BCData* bcData = nsnull;
1049 switch(aSide) {
1050 case NS_SIDE_BOTTOM:
1051 aRowIndex++;
1052 case NS_SIDE_TOP:
1053 cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex);
1054 if (cellData) {
1055 bcData = &cellData->mData;
1057 else {
1058 NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error");
1059 // try the next row group
1060 nsCellMap* cellMap = aCellMap.GetNextSibling();
1061 if (cellMap) {
1062 cellData = (BCCellData*)cellMap->GetDataAt(0, aColIndex);
1063 if (cellData) {
1064 bcData = &cellData->mData;
1066 else {
1067 bcData = GetBottomMostBorder(aColIndex);
1071 break;
1072 case NS_SIDE_RIGHT:
1073 aColIndex++;
1074 case NS_SIDE_LEFT:
1075 cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex);
1076 if (cellData) {
1077 bcData = &cellData->mData;
1079 else {
1080 NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error");
1081 bcData = GetRightMostBorder(aRowIndex);
1083 break;
1085 return bcData;
1088 // store the aSide border segment at coord = (aRowIndex, aColIndex). For top/left, store
1089 // the info at coord. For bottom/left store it at the adjacent location so that it is
1090 // top/left at that location. If the new location is at the right or bottom edge of the
1091 // table, then store it one of the special arrays (right most borders, bottom most borders).
1092 void
1093 nsTableCellMap::SetBCBorderEdge(PRUint8 aSide,
1094 nsCellMap& aCellMap,
1095 PRUint32 aCellMapStart,
1096 PRUint32 aRowIndex,
1097 PRUint32 aColIndex,
1098 PRUint32 aLength,
1099 BCBorderOwner aOwner,
1100 nscoord aSize,
1101 PRBool aChanged)
1103 if (!mBCInfo) ABORT0();
1105 BCCellData* cellData;
1106 PRInt32 lastIndex, xIndex, yIndex;
1107 PRInt32 xPos = aColIndex;
1108 PRInt32 yPos = aRowIndex;
1109 PRInt32 rgYPos = aRowIndex - aCellMapStart;
1110 PRBool changed;
1112 switch(aSide) {
1113 case NS_SIDE_BOTTOM:
1114 rgYPos++;
1115 yPos++;
1116 case NS_SIDE_TOP:
1117 lastIndex = xPos + aLength - 1;
1118 for (xIndex = xPos; xIndex <= lastIndex; xIndex++) {
1119 changed = aChanged && (xIndex == xPos);
1120 BCData* bcData = nsnull;
1121 cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xIndex);
1122 if (!cellData) {
1123 PRInt32 numRgRows = aCellMap.GetRowCount();
1124 if (yPos < numRgRows) { // add a dead cell data
1125 nsRect damageArea;
1126 cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea); if (!cellData) ABORT0();
1128 else {
1129 NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error");
1130 // try the next non empty row group
1131 nsCellMap* cellMap = aCellMap.GetNextSibling();
1132 while (cellMap && (0 == cellMap->GetRowCount())) {
1133 cellMap = cellMap->GetNextSibling();
1135 if (cellMap) {
1136 cellData = (BCCellData*)cellMap->GetDataAt(0, xIndex);
1137 if (!cellData) { // add a dead cell
1138 nsRect damageArea;
1139 cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea);
1142 else { // must be at the end of the table
1143 bcData = GetBottomMostBorder(xIndex);
1147 if (!bcData && cellData) {
1148 bcData = &cellData->mData;
1150 if (bcData) {
1151 bcData->SetTopEdge(aOwner, aSize, changed);
1153 else NS_ERROR("Cellmap: Top edge not found");
1155 break;
1156 case NS_SIDE_RIGHT:
1157 xPos++;
1158 case NS_SIDE_LEFT:
1159 // since top, bottom borders were set, there should already be a cellData entry
1160 lastIndex = rgYPos + aLength - 1;
1161 for (yIndex = rgYPos; yIndex <= lastIndex; yIndex++) {
1162 changed = aChanged && (yIndex == rgYPos);
1163 cellData = (BCCellData*)aCellMap.GetDataAt(yIndex, xPos);
1164 if (cellData) {
1165 cellData->mData.SetLeftEdge(aOwner, aSize, changed);
1167 else {
1168 NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error");
1169 BCData* bcData = GetRightMostBorder(yIndex + aCellMapStart);
1170 if (bcData) {
1171 bcData->SetLeftEdge(aOwner, aSize, changed);
1173 else NS_ERROR("Cellmap: Left edge not found");
1176 break;
1180 // store corner info (aOwner, aSubSize, aBevel). For aCorner = eTopLeft, store the info at
1181 // (aRowIndex, aColIndex). For eTopRight, store it in the entry to the right where
1182 // it would be top left. For eBottomRight, store it in the entry to the bottom. etc.
1183 void
1184 nsTableCellMap::SetBCBorderCorner(Corner aCorner,
1185 nsCellMap& aCellMap,
1186 PRUint32 aCellMapStart,
1187 PRUint32 aRowIndex,
1188 PRUint32 aColIndex,
1189 PRUint8 aOwner,
1190 nscoord aSubSize,
1191 PRBool aBevel,
1192 PRBool aIsBottomRight)
1194 if (!mBCInfo) ABORT0();
1196 if (aIsBottomRight) {
1197 mBCInfo->mLowerRightCorner.SetCorner(aSubSize, aOwner, aBevel);
1198 return;
1201 PRInt32 xPos = aColIndex;
1202 PRInt32 yPos = aRowIndex;
1203 PRInt32 rgYPos = aRowIndex - aCellMapStart;
1205 if (eTopRight == aCorner) {
1206 xPos++;
1208 else if (eBottomRight == aCorner) {
1209 xPos++;
1210 rgYPos++;
1211 yPos++;
1213 else if (eBottomLeft == aCorner) {
1214 rgYPos++;
1215 yPos++;
1218 BCCellData* cellData = nsnull;
1219 BCData* bcData = nsnull;
1220 if (GetColCount() <= xPos) {
1221 NS_ASSERTION(xPos == GetColCount(), "program error");
1222 // at the right edge of the table as we checked the corner before
1223 NS_ASSERTION(!aIsBottomRight, "should be handled before");
1224 bcData = GetRightMostBorder(yPos);
1226 else {
1227 cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xPos);
1228 if (!cellData) {
1229 PRInt32 numRgRows = aCellMap.GetRowCount();
1230 if (yPos < numRgRows) { // add a dead cell data
1231 nsRect damageArea;
1232 cellData = (BCCellData*)aCellMap.AppendCell(*this, nsnull, rgYPos, PR_FALSE, damageArea);
1234 else {
1235 // try the next non empty row group
1236 nsCellMap* cellMap = aCellMap.GetNextSibling();
1237 while (cellMap && (0 == cellMap->GetRowCount())) {
1238 cellMap = cellMap->GetNextSibling();
1240 if (cellMap) {
1241 cellData = (BCCellData*)cellMap->GetDataAt(0, xPos);
1242 if (!cellData) { // add a dead cell
1243 nsRect damageArea;
1244 cellData = (BCCellData*)cellMap->AppendCell(*this, nsnull, 0, PR_FALSE, damageArea);
1247 else { // must be a the bottom of the table
1248 bcData = GetBottomMostBorder(xPos);
1253 if (!bcData && cellData) {
1254 bcData = &cellData->mData;
1256 if (bcData) {
1257 bcData->SetCorner(aSubSize, aOwner, aBevel);
1259 else NS_ERROR("program error: Corner not found");
1262 nsCellMap::nsCellMap(nsTableRowGroupFrame& aRowGroup, PRBool aIsBC)
1263 : mRows(8), mContentRowCount(0), mRowGroupFrame(&aRowGroup),
1264 mNextSibling(nsnull), mIsBC(aIsBC),
1265 mPresContext(aRowGroup.PresContext())
1267 MOZ_COUNT_CTOR(nsCellMap);
1268 NS_ASSERTION(mPresContext, "Must have prescontext");
1271 nsCellMap::~nsCellMap()
1273 MOZ_COUNT_DTOR(nsCellMap);
1275 PRUint32 mapRowCount = mRows.Length();
1276 for (PRUint32 rowX = 0; rowX < mapRowCount; rowX++) {
1277 CellDataArray &row = mRows[rowX];
1278 PRUint32 colCount = row.Length();
1279 for (PRUint32 colX = 0; colX < colCount; colX++) {
1280 DestroyCellData(row[colX]);
1285 /* static */
1286 nsresult
1287 nsCellMap::Init()
1289 NS_ASSERTION(!sEmptyRow, "How did that happen?");
1290 sEmptyRow = new nsCellMap::CellDataArray();
1291 NS_ENSURE_TRUE(sEmptyRow, NS_ERROR_OUT_OF_MEMORY);
1293 return NS_OK;
1296 /* static */
1297 void
1298 nsCellMap::Shutdown()
1300 delete sEmptyRow;
1301 sEmptyRow = nsnull;
1304 nsTableCellFrame*
1305 nsCellMap::GetCellFrame(PRInt32 aRowIndexIn,
1306 PRInt32 aColIndexIn,
1307 CellData& aData,
1308 PRBool aUseRowIfOverlap) const
1310 PRInt32 rowIndex = aRowIndexIn - aData.GetRowSpanOffset();
1311 PRInt32 colIndex = aColIndexIn - aData.GetColSpanOffset();
1312 if (aData.IsOverlap()) {
1313 if (aUseRowIfOverlap) {
1314 colIndex = aColIndexIn;
1316 else {
1317 rowIndex = aRowIndexIn;
1321 CellData* data =
1322 mRows.SafeElementAt(rowIndex, *sEmptyRow).SafeElementAt(colIndex);
1323 if (data) {
1324 return data->GetCellFrame();
1326 return nsnull;
1329 PRInt32
1330 nsCellMap::GetIndexByRowAndColumn(PRInt32 aColCount,
1331 PRInt32 aRow, PRInt32 aColumn) const
1333 PRInt32 index = -1;
1335 if (aRow >= mRows.Length())
1336 return index;
1338 PRInt32 lastColsIdx = aColCount - 1;
1339 for (PRInt32 rowIdx = 0; rowIdx <= aRow; rowIdx++) {
1340 const CellDataArray& row = mRows[rowIdx];
1341 PRInt32 colCount = (rowIdx == aRow) ? aColumn : lastColsIdx;
1343 for (PRInt32 colIdx = 0; colIdx <= colCount; colIdx++) {
1344 CellData* data = row.SafeElementAt(colIdx);
1345 if (data && data->IsOrig())
1346 index++;
1350 return index;
1353 void
1354 nsCellMap::GetRowAndColumnByIndex(PRInt32 aColCount, PRInt32 aIndex,
1355 PRInt32 *aRow, PRInt32 *aColumn) const
1357 *aRow = -1;
1358 *aColumn = -1;
1360 PRInt32 index = aIndex;
1361 PRInt32 rowCount = mRows.Length();
1363 for (PRInt32 rowIdx = 0; rowIdx < rowCount; rowIdx++) {
1364 const CellDataArray& row = mRows[rowIdx];
1366 for (PRInt32 colIdx = 0; colIdx < aColCount; colIdx++) {
1367 CellData* data = row.SafeElementAt(colIdx);
1368 if (data && data->IsOrig())
1369 index--;
1371 if (index < 0) {
1372 *aRow = rowIdx;
1373 *aColumn = colIdx;
1374 return;
1380 PRBool nsCellMap::Grow(nsTableCellMap& aMap,
1381 PRInt32 aNumRows,
1382 PRInt32 aRowIndex)
1384 NS_ASSERTION(aNumRows >= 1, "Why are we calling this?");
1386 // Get the number of cols we want to use for preallocating the row arrays.
1387 PRInt32 numCols = aMap.GetColCount();
1388 if (numCols == 0) {
1389 numCols = 4;
1391 PRUint32 startRowIndex = (aRowIndex >= 0) ? aRowIndex : mRows.Length();
1392 NS_ASSERTION(startRowIndex <= mRows.Length(), "Missing grow call inbetween");
1394 return mRows.InsertElementsAt(startRowIndex, aNumRows, numCols) != nsnull;
1397 void nsCellMap::GrowRow(CellDataArray& aRow,
1398 PRInt32 aNumCols)
1401 // Have to have the cast to get the template to do the right thing.
1402 aRow.InsertElementsAt(aRow.Length(), aNumCols, (CellData*)nsnull);
1405 void
1406 nsCellMap::InsertRows(nsTableCellMap& aMap,
1407 nsVoidArray& aRows,
1408 PRInt32 aFirstRowIndex,
1409 PRBool aConsiderSpans,
1410 nsRect& aDamageArea)
1412 PRInt32 numCols = aMap.GetColCount();
1413 NS_ASSERTION(aFirstRowIndex >= 0, "nsCellMap::InsertRows called with negative rowIndex");
1414 if (PRUint32(aFirstRowIndex) > mRows.Length()) {
1415 // create (aFirstRowIndex - mRows.Length()) empty rows up to aFirstRowIndex
1416 PRInt32 numEmptyRows = aFirstRowIndex - mRows.Length();
1417 if (!Grow(aMap, numEmptyRows)) {
1418 return;
1422 if (!aConsiderSpans) {
1423 // update mContentRowCount, since non-empty rows will be added
1424 mContentRowCount = PR_MAX(aFirstRowIndex, mContentRowCount);
1425 ExpandWithRows(aMap, aRows, aFirstRowIndex, aDamageArea);
1426 return;
1429 // if any cells span into or out of the row being inserted, then rebuild
1430 PRBool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex,
1431 aFirstRowIndex, 0, numCols - 1);
1433 // update mContentRowCount, since non-empty rows will be added
1434 mContentRowCount = PR_MAX(aFirstRowIndex, mContentRowCount);
1436 // if any of the new cells span out of the new rows being added, then rebuild
1437 // XXX it would be better to only rebuild the portion of the map that follows the new rows
1438 if (!spansCauseRebuild && (PRUint32(aFirstRowIndex) < mRows.Length())) {
1439 spansCauseRebuild = CellsSpanOut(aRows);
1442 if (spansCauseRebuild) {
1443 aMap.RebuildConsideringRows(this, aFirstRowIndex, &aRows, 0, aDamageArea);
1445 else {
1446 ExpandWithRows(aMap, aRows, aFirstRowIndex, aDamageArea);
1450 void
1451 nsCellMap::RemoveRows(nsTableCellMap& aMap,
1452 PRInt32 aFirstRowIndex,
1453 PRInt32 aNumRowsToRemove,
1454 PRBool aConsiderSpans,
1455 nsRect& aDamageArea)
1457 PRInt32 numRows = mRows.Length();
1458 PRInt32 numCols = aMap.GetColCount();
1460 if (aFirstRowIndex >= numRows) {
1461 // reduce the content based row count based on the function arguments
1462 // as they are known to be real rows even if the cell map did not create
1463 // rows for them before.
1464 mContentRowCount -= aNumRowsToRemove;
1465 return;
1467 if (!aConsiderSpans) {
1468 ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea);
1469 return;
1471 PRInt32 endRowIndex = aFirstRowIndex + aNumRowsToRemove - 1;
1472 if (endRowIndex >= numRows) {
1473 NS_ERROR("nsCellMap::RemoveRows tried to remove too many rows");
1474 endRowIndex = numRows - 1;
1476 PRBool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex, endRowIndex,
1477 0, numCols - 1);
1479 if (spansCauseRebuild) {
1480 aMap.RebuildConsideringRows(this, aFirstRowIndex, nsnull, aNumRowsToRemove, aDamageArea);
1482 else {
1483 ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aDamageArea);
1490 CellData*
1491 nsCellMap::AppendCell(nsTableCellMap& aMap,
1492 nsTableCellFrame* aCellFrame,
1493 PRInt32 aRowIndex,
1494 PRBool aRebuildIfNecessary,
1495 nsRect& aDamageArea,
1496 PRInt32* aColToBeginSearch)
1498 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1499 PRInt32 origNumMapRows = mRows.Length();
1500 PRInt32 origNumCols = aMap.GetColCount();
1501 PRBool zeroRowSpan = PR_FALSE;
1502 PRInt32 rowSpan = (aCellFrame) ? GetRowSpanForNewCell(aCellFrame, aRowIndex,
1503 zeroRowSpan) : 1;
1504 // add new rows if necessary
1505 PRInt32 endRowIndex = aRowIndex + rowSpan - 1;
1506 if (endRowIndex >= origNumMapRows) {
1507 // XXXbz handle allocation failures?
1508 Grow(aMap, 1 + endRowIndex - origNumMapRows);
1511 // get the first null or dead CellData in the desired row. It will equal origNumCols if there are none
1512 CellData* origData = nsnull;
1513 PRInt32 startColIndex = 0;
1514 if (aColToBeginSearch)
1515 startColIndex = *aColToBeginSearch;
1516 for (; startColIndex < origNumCols; startColIndex++) {
1517 CellData* data = GetDataAt(aRowIndex, startColIndex);
1518 if (!data)
1519 break;
1520 if (data->IsDead()) {
1521 origData = data;
1522 break;
1524 if (data->IsZeroColSpan() ) {
1525 // appending a cell collapses zerospans.
1526 CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex);
1527 // ask again for the data as it should be modified
1528 origData = GetDataAt(aRowIndex, startColIndex);
1529 NS_ASSERTION(origData->IsDead(),
1530 "The cellposition should have been cleared");
1531 break;
1534 // We found the place to append the cell, when the next cell is appended
1535 // the next search does not need to duplicate the search but can start
1536 // just at the next cell.
1537 if (aColToBeginSearch)
1538 *aColToBeginSearch = startColIndex + 1;
1540 PRBool zeroColSpan = PR_FALSE;
1541 PRInt32 colSpan = (aCellFrame) ?
1542 GetColSpanForNewCell(*aCellFrame, zeroColSpan) : 1;
1543 if (zeroColSpan) {
1544 aMap.mTableFrame.SetHasZeroColSpans(PR_TRUE);
1545 aMap.mTableFrame.SetNeedColSpanExpansion(PR_TRUE);
1548 // if the new cell could potentially span into other rows and collide with
1549 // originating cells there, we will play it safe and just rebuild the map
1550 if (aRebuildIfNecessary && (aRowIndex < mContentRowCount - 1) && (rowSpan > 1)) {
1551 nsAutoVoidArray newCellArray;
1552 newCellArray.AppendElement(aCellFrame);
1553 aMap.RebuildConsideringCells(this, &newCellArray, aRowIndex, startColIndex, PR_TRUE, aDamageArea);
1554 return origData;
1556 mContentRowCount = PR_MAX(mContentRowCount, aRowIndex + 1);
1558 // add new cols to the table map if necessary
1559 PRInt32 endColIndex = startColIndex + colSpan - 1;
1560 if (endColIndex >= origNumCols) {
1561 NS_ASSERTION(aCellFrame, "dead cells should not require new columns");
1562 aMap.AddColsAtEnd(1 + endColIndex - origNumCols);
1565 // Setup CellData for this cell
1566 if (origData) {
1567 NS_ASSERTION(origData->IsDead(), "replacing a non dead cell is a memory leak");
1568 if (aCellFrame) { // do nothing to replace a dead cell with a dead cell
1569 origData->Init(aCellFrame);
1570 // we are replacing a dead cell, increase the number of cells
1571 // originating at this column
1572 nsColInfo* colInfo = aMap.GetColInfoAt(startColIndex);
1573 NS_ASSERTION(colInfo, "access to a non existing column");
1574 if (colInfo) {
1575 colInfo->mNumCellsOrig++;
1579 else {
1580 origData = AllocCellData(aCellFrame);
1581 if (!origData) ABORT1(origData);
1582 SetDataAt(aMap, *origData, aRowIndex, startColIndex);
1585 SetDamageArea(startColIndex, aRowIndex, 1 + endColIndex - startColIndex, 1 + endRowIndex - aRowIndex, aDamageArea);
1587 if (!aCellFrame) {
1588 return origData;
1591 // initialize the cell frame
1592 aCellFrame->SetColIndex(startColIndex);
1594 // Create CellData objects for the rows that this cell spans. Set
1595 // their mOrigCell to nsnull and their mSpanData to point to data.
1596 for (PRInt32 rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1597 // The row at rowX will need to have at least endColIndex columns
1598 mRows[rowX].SetCapacity(endColIndex);
1599 for (PRInt32 colX = startColIndex; colX <= endColIndex; colX++) {
1600 if ((rowX != aRowIndex) || (colX != startColIndex)) { // skip orig cell data done above
1601 CellData* cellData = GetDataAt(rowX, colX);
1602 if (cellData) {
1603 if (cellData->IsOrig()) {
1604 NS_ERROR("cannot overlap originating cell");
1605 continue;
1607 if (rowX > aRowIndex) { // row spanning into cell
1608 if (cellData->IsRowSpan()) {
1609 // do nothing, this can be caused by rowspan which is overlapped
1610 // by a another cell with a rowspan and a colspan
1612 else {
1613 cellData->SetRowSpanOffset(rowX - aRowIndex);
1614 if (zeroRowSpan) {
1615 cellData->SetZeroRowSpan(PR_TRUE);
1619 if (colX > startColIndex) { // col spanning into cell
1620 if (!cellData->IsColSpan()) {
1621 if (cellData->IsRowSpan()) {
1622 cellData->SetOverlap(PR_TRUE);
1624 cellData->SetColSpanOffset(colX - startColIndex);
1625 if (zeroColSpan) {
1626 cellData->SetZeroColSpan(PR_TRUE);
1629 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1630 colInfo->mNumCellsSpan++;
1634 else {
1635 cellData = AllocCellData(nsnull);
1636 if (!cellData) return origData;
1637 if (rowX > aRowIndex) {
1638 cellData->SetRowSpanOffset(rowX - aRowIndex);
1639 if (zeroRowSpan) {
1640 cellData->SetZeroRowSpan(PR_TRUE);
1643 if (colX > startColIndex) {
1644 cellData->SetColSpanOffset(colX - startColIndex);
1645 if (zeroColSpan) {
1646 cellData->SetZeroColSpan(PR_TRUE);
1649 SetDataAt(aMap, *cellData, rowX, colX);
1654 #ifdef DEBUG_TABLE_CELLMAP
1655 printf("appended cell=%p row=%d \n", aCellFrame, aRowIndex);
1656 aMap.Dump();
1657 #endif
1658 return origData;
1661 void nsCellMap::CollapseZeroColSpan(nsTableCellMap& aMap,
1662 CellData* aOrigData,
1663 PRInt32 aRowIndex,
1664 PRInt32 aColIndex)
1666 // if after a colspan = 0 cell another cell is appended in a row the html 4
1667 // spec is already violated. In principle one should then append the cell
1668 // after the last column but then the zero spanning cell would also have
1669 // to grow. The only plausible way to break this cycle is ignore the zero
1670 // colspan and reset the cell to colspan = 1.
1672 NS_ASSERTION(aOrigData && aOrigData->IsZeroColSpan(),
1673 "zero colspan should have been passed");
1674 // find the originating cellframe
1675 nsTableCellFrame* cell = GetCellFrame(aRowIndex, aColIndex, *aOrigData, PR_TRUE);
1676 NS_ASSERTION(cell, "originating cell not found");
1678 // find the clearing region
1679 PRInt32 startRowIndex = aRowIndex - aOrigData->GetRowSpanOffset();
1680 PRBool zeroSpan;
1681 PRInt32 rowSpan = GetRowSpanForNewCell(cell, startRowIndex, zeroSpan);
1682 PRInt32 endRowIndex = startRowIndex + rowSpan;
1684 PRInt32 origColIndex = aColIndex - aOrigData->GetColSpanOffset();
1685 PRInt32 endColIndex = origColIndex +
1686 GetEffectiveColSpan(aMap, startRowIndex,
1687 origColIndex, zeroSpan);
1688 for (PRInt32 colX = origColIndex +1; colX < endColIndex; colX++) {
1689 // Start the collapse just after the originating cell, since
1690 // we're basically making the originating cell act as if it
1691 // has colspan="1".
1692 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1693 colInfo->mNumCellsSpan -= rowSpan;
1695 for (PRInt32 rowX = startRowIndex; rowX < endRowIndex; rowX++)
1697 CellData* data = mRows[rowX][colX];
1698 NS_ASSERTION(data->IsZeroColSpan(),
1699 "Overwriting previous data - memory leak");
1700 data->Init(nsnull); // mark the cell as a dead cell.
1705 PRBool nsCellMap::CellsSpanOut(nsVoidArray& aRows) const
1707 PRInt32 numNewRows = aRows.Count();
1708 for (PRInt32 rowX = 0; rowX < numNewRows; rowX++) {
1709 nsIFrame* rowFrame = (nsIFrame *) aRows.ElementAt(rowX);
1710 nsIFrame* cellFrame = rowFrame->GetFirstChild(nsnull);
1711 while (cellFrame) {
1712 if (IS_TABLE_CELL(cellFrame->GetType())) {
1713 PRBool zeroSpan;
1714 PRInt32 rowSpan = GetRowSpanForNewCell((nsTableCellFrame*) cellFrame,
1715 rowX, zeroSpan);
1716 if (rowX + rowSpan > numNewRows) {
1717 return PR_TRUE;
1720 cellFrame = cellFrame->GetNextSibling();
1723 return PR_FALSE;
1726 // return PR_TRUE if any cells have rows spans into or out of the region
1727 // defined by the row and col indices or any cells have colspans into the region
1728 PRBool nsCellMap::CellsSpanInOrOut(PRInt32 aStartRowIndex,
1729 PRInt32 aEndRowIndex,
1730 PRInt32 aStartColIndex,
1731 PRInt32 aEndColIndex) const
1734 * this routine will watch the cells adjacent to the region or at the edge
1735 * they are marked with *. The routine will verify whether they span in or
1736 * are spanned out.
1738 * startCol endCol
1739 * r1c1 r1c2 r1c3 r1c4 r1c5 r1rc6 r1c7
1740 * startrow r2c1 r2c2 *r2c3 *r2c4 *r2c5 *r2rc6 r2c7
1741 * endrow r3c1 r3c2 *r3c3 r3c4 r3c5 *r3rc6 r3c7
1742 * r4c1 r4c2 *r4c3 *r4c4 *r4c5 r4rc6 r4c7
1743 * r5c1 r5c2 r5c3 r5c4 r5c5 r5rc6 r5c7
1746 PRInt32 numRows = mRows.Length(); // use the cellmap rows to determine the
1747 // current cellmap extent.
1748 for (PRInt32 colX = aStartColIndex; colX <= aEndColIndex; colX++) {
1749 CellData* cellData;
1750 if (aStartRowIndex > 0) {
1751 cellData = GetDataAt(aStartRowIndex, colX);
1752 if (cellData && (cellData->IsRowSpan())) {
1753 return PR_TRUE; // there is a row span into the region
1755 if ((aStartRowIndex >= mContentRowCount) && (mContentRowCount > 0)) {
1756 cellData = GetDataAt(mContentRowCount - 1, colX);
1757 if (cellData && cellData->IsZeroRowSpan()) {
1758 return PR_TRUE; // When we expand the zerospan it'll span into our row
1762 if (aEndRowIndex < numRows - 1) { // is there anything below aEndRowIndex
1763 cellData = GetDataAt(aEndRowIndex + 1, colX);
1764 if ((cellData) && (cellData->IsRowSpan())) {
1765 return PR_TRUE; // there is a row span out of the region
1768 else {
1769 cellData = GetDataAt(aEndRowIndex, colX);
1770 if ((cellData) && (cellData->IsRowSpan()) && (mContentRowCount < numRows)) {
1771 return PR_TRUE; // this cell might be the cause of a dead row
1775 if (aStartColIndex > 0) {
1776 for (PRInt32 rowX = aStartRowIndex; rowX <= aEndRowIndex; rowX++) {
1777 CellData* cellData = GetDataAt(rowX, aStartColIndex);
1778 if (cellData && (cellData->IsColSpan())) {
1779 return PR_TRUE; // there is a col span into the region
1781 cellData = GetDataAt(rowX, aEndColIndex + 1);
1782 if (cellData && (cellData->IsColSpan())) {
1783 return PR_TRUE; // there is a col span out of the region
1787 return PR_FALSE;
1790 void nsCellMap::InsertCells(nsTableCellMap& aMap,
1791 nsVoidArray& aCellFrames,
1792 PRInt32 aRowIndex,
1793 PRInt32 aColIndexBefore,
1794 nsRect& aDamageArea)
1796 if (aCellFrames.Count() == 0) return;
1797 NS_ASSERTION(aColIndexBefore >= -1, "index out of range");
1798 PRInt32 numCols = aMap.GetColCount();
1799 if (aColIndexBefore >= numCols) {
1800 NS_ERROR("Inserting instead of appending cells indicates a serious cellmap error");
1801 aColIndexBefore = numCols - 1;
1804 // get the starting col index of the 1st new cells
1805 PRInt32 startColIndex;
1806 for (startColIndex = aColIndexBefore + 1; startColIndex < numCols; startColIndex++) {
1807 CellData* data = GetDataAt(aRowIndex, startColIndex);
1808 if (!data || data->IsOrig() || data->IsDead()) {
1809 // // Not a span. Stop.
1810 break;
1812 if (data->IsZeroColSpan()) {
1813 // Zero colspans collapse. Stop in this case too.
1814 CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex);
1815 break;
1819 // record whether inserted cells are going to cause complications due
1820 // to existing row spans, col spans or table sizing.
1821 PRBool spansCauseRebuild = PR_FALSE;
1823 // check that all cells have the same row span
1824 PRInt32 numNewCells = aCellFrames.Count();
1825 PRBool zeroRowSpan = PR_FALSE;
1826 PRInt32 rowSpan = 0;
1827 for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) {
1828 nsTableCellFrame* cell = (nsTableCellFrame*) aCellFrames.ElementAt(cellX);
1829 PRInt32 rowSpan2 = GetRowSpanForNewCell(cell, aRowIndex, zeroRowSpan);
1830 if (rowSpan == 0) {
1831 rowSpan = rowSpan2;
1833 else if (rowSpan != rowSpan2) {
1834 spansCauseRebuild = PR_TRUE;
1835 break;
1839 // check if the new cells will cause the table to add more rows
1840 if (!spansCauseRebuild) {
1841 if (mRows.Length() < PRUint32(aRowIndex + rowSpan)) {
1842 spansCauseRebuild = PR_TRUE;
1846 if (!spansCauseRebuild) {
1847 spansCauseRebuild = CellsSpanInOrOut(aRowIndex, aRowIndex + rowSpan - 1,
1848 startColIndex, numCols - 1);
1851 if (spansCauseRebuild) {
1852 aMap.RebuildConsideringCells(this, &aCellFrames, aRowIndex, startColIndex, PR_TRUE, aDamageArea);
1854 else {
1855 ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan, zeroRowSpan, aDamageArea);
1859 void
1860 nsCellMap::ExpandWithRows(nsTableCellMap& aMap,
1861 nsVoidArray& aRowFrames,
1862 PRInt32 aStartRowIndexIn,
1863 nsRect& aDamageArea)
1865 PRInt32 startRowIndex = (aStartRowIndexIn >= 0) ? aStartRowIndexIn : 0;
1866 NS_ASSERTION(PRUint32(startRowIndex) <= mRows.Length(), "caller should have grown cellmap before");
1868 PRInt32 numNewRows = aRowFrames.Count();
1869 mContentRowCount += numNewRows;
1871 PRInt32 endRowIndex = startRowIndex + numNewRows - 1;
1873 // shift the rows after startRowIndex down and insert empty rows that will
1874 // be filled via the AppendCell call below
1875 if (!Grow(aMap, numNewRows, startRowIndex)) {
1876 return;
1880 PRInt32 newRowIndex = 0;
1881 for (PRInt32 rowX = startRowIndex; rowX <= endRowIndex; rowX++) {
1882 nsTableRowFrame* rFrame = (nsTableRowFrame *)aRowFrames.ElementAt(newRowIndex);
1883 // append cells
1884 nsIFrame* cFrame = rFrame->GetFirstChild(nsnull);
1885 PRInt32 colIndex = 0;
1886 while (cFrame) {
1887 if (IS_TABLE_CELL(cFrame->GetType())) {
1888 AppendCell(aMap, (nsTableCellFrame *)cFrame, rowX, PR_FALSE, aDamageArea, &colIndex);
1890 cFrame = cFrame->GetNextSibling();
1892 newRowIndex++;
1895 SetDamageArea(0, startRowIndex, aMap.GetColCount(), 1 + endRowIndex - startRowIndex, aDamageArea);
1898 void nsCellMap::ExpandWithCells(nsTableCellMap& aMap,
1899 nsVoidArray& aCellFrames,
1900 PRInt32 aRowIndex,
1901 PRInt32 aColIndex,
1902 PRInt32 aRowSpan, // same for all cells
1903 PRBool aRowSpanIsZero,
1904 nsRect& aDamageArea)
1906 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1907 PRInt32 endRowIndex = aRowIndex + aRowSpan - 1;
1908 PRInt32 startColIndex = aColIndex;
1909 PRInt32 endColIndex = aColIndex;
1910 PRInt32 numCells = aCellFrames.Count();
1911 PRInt32 totalColSpan = 0;
1913 // add cellData entries for the space taken up by the new cells
1914 for (PRInt32 cellX = 0; cellX < numCells; cellX++) {
1915 nsTableCellFrame* cellFrame = (nsTableCellFrame*) aCellFrames.ElementAt(cellX);
1916 CellData* origData = AllocCellData(cellFrame); // the originating cell
1917 if (!origData) return;
1919 // set the starting and ending col index for the new cell
1920 PRBool zeroColSpan = PR_FALSE;
1921 PRInt32 colSpan = GetColSpanForNewCell(*cellFrame, zeroColSpan);
1922 if (zeroColSpan) {
1923 aMap.mTableFrame.SetHasZeroColSpans(PR_TRUE);
1924 aMap.mTableFrame.SetNeedColSpanExpansion(PR_TRUE);
1926 totalColSpan += colSpan;
1927 if (cellX == 0) {
1928 endColIndex = aColIndex + colSpan - 1;
1930 else {
1931 startColIndex = endColIndex + 1;
1932 endColIndex = startColIndex + colSpan - 1;
1935 // add the originating cell data and any cell data corresponding to row/col spans
1936 for (PRInt32 rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1937 CellDataArray& row = mRows[rowX];
1938 // Pre-allocate all the cells we'll need in this array, setting
1939 // them to null.
1940 // Have to have the cast to get the template to do the right thing.
1941 PRUint32 insertionIndex = row.Length();
1942 if (insertionIndex > aColIndex) {
1943 insertionIndex = aColIndex;
1945 if (!row.InsertElementsAt(insertionIndex, endColIndex - insertionIndex + 1,
1946 (CellData*)nsnull) &&
1947 rowX == aRowIndex) {
1948 // Failed to insert the slots, and this is the very first row. That
1949 // means that we need to clean up |origData| before returning, since
1950 // the cellmap doesn't own it yet.
1951 DestroyCellData(origData);
1952 return;
1955 for (PRInt32 colX = aColIndex; colX <= endColIndex; colX++) {
1956 CellData* data = origData;
1957 if ((rowX != aRowIndex) || (colX != startColIndex)) {
1958 data = AllocCellData(nsnull);
1959 if (!data) return;
1960 if (rowX > aRowIndex) {
1961 data->SetRowSpanOffset(rowX - aRowIndex);
1962 if (aRowSpanIsZero) {
1963 data->SetZeroRowSpan(PR_TRUE);
1966 if (colX > startColIndex) {
1967 data->SetColSpanOffset(colX - startColIndex);
1968 if (zeroColSpan) {
1969 data->SetZeroColSpan(PR_TRUE);
1973 SetDataAt(aMap, *data, rowX, colX);
1976 cellFrame->SetColIndex(startColIndex);
1978 PRInt32 damageHeight = PR_MIN(GetRowGroup()->GetRowCount() - aRowIndex, aRowSpan);
1979 SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, damageHeight, aDamageArea);
1981 PRInt32 rowX;
1983 // update the row and col info due to shifting
1984 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1985 CellDataArray& row = mRows[rowX];
1986 PRUint32 numCols = row.Length();
1987 PRUint32 colX;
1988 for (colX = aColIndex + totalColSpan; colX < numCols; colX++) {
1989 CellData* data = row[colX];
1990 if (data) {
1991 // increase the origin and span counts beyond the spanned cols
1992 if (data->IsOrig()) {
1993 // a cell that gets moved needs adjustment as well as it new orignating col
1994 data->GetCellFrame()->SetColIndex(colX);
1995 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1996 colInfo->mNumCellsOrig++;
1998 if (data->IsColSpan()) {
1999 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2000 colInfo->mNumCellsSpan++;
2003 // decrease the origin and span counts within the spanned cols
2004 PRInt32 colX2 = colX - totalColSpan;
2005 nsColInfo* colInfo2 = aMap.GetColInfoAt(colX2);
2006 if (data->IsOrig()) {
2007 // the old originating col of a moved cell needs adjustment
2008 colInfo2->mNumCellsOrig--;
2010 if (data->IsColSpan()) {
2011 colInfo2->mNumCellsSpan--;
2018 void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap,
2019 PRInt32 aStartRowIndex,
2020 PRInt32 aNumRowsToRemove,
2021 nsRect& aDamageArea)
2023 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2024 PRInt32 endRowIndex = aStartRowIndex + aNumRowsToRemove - 1;
2025 PRUint32 colCount = aMap.GetColCount();
2026 for (PRInt32 rowX = endRowIndex; rowX >= aStartRowIndex; --rowX) {
2027 CellDataArray& row = mRows[rowX];
2028 PRUint32 colX;
2029 for (colX = 0; colX < colCount; colX++) {
2030 CellData* data = row.SafeElementAt(colX);
2031 if (data) {
2032 // Adjust the column counts.
2033 if (data->IsOrig()) {
2034 // Decrement the column count.
2035 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2036 colInfo->mNumCellsOrig--;
2038 // colspan=0 is only counted as a spanned cell in the 1st col it spans
2039 else if (data->IsColSpan()) {
2040 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2041 colInfo->mNumCellsSpan--;
2046 PRUint32 rowLength = row.Length();
2047 // Delete our row information.
2048 for (colX = 0; colX < rowLength; colX++) {
2049 DestroyCellData(row[colX]);
2052 mRows.RemoveElementAt(rowX);
2054 // Decrement our row and next available index counts.
2055 mContentRowCount--;
2057 aMap.RemoveColsAtEnd();
2059 SetDamageArea(0, aStartRowIndex, aMap.GetColCount(), 0, aDamageArea);
2062 PRInt32 nsCellMap::GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd,
2063 PRBool& aIsZeroColSpan) const
2065 aIsZeroColSpan = PR_FALSE;
2066 PRInt32 colSpan = aCellFrameToAdd.GetColSpan();
2067 if (0 == colSpan) {
2068 colSpan = 1; // set the min colspan it will be expanded later
2069 aIsZeroColSpan = PR_TRUE;
2071 return colSpan;
2074 PRInt32 nsCellMap::GetEffectiveColSpan(const nsTableCellMap& aMap,
2075 PRInt32 aRowIndex,
2076 PRInt32 aColIndex,
2077 PRBool& aZeroColSpan) const
2079 PRInt32 numColsInTable = aMap.GetColCount();
2080 aZeroColSpan = PR_FALSE;
2081 PRInt32 colSpan = 1;
2082 if (PRUint32(aRowIndex) >= mRows.Length()) {
2083 return colSpan;
2086 const CellDataArray& row = mRows[aRowIndex];
2087 PRInt32 colX;
2088 CellData* data;
2089 PRInt32 maxCols = numColsInTable;
2090 PRBool hitOverlap = PR_FALSE; // XXX this is not ever being set to PR_TRUE
2091 for (colX = aColIndex + 1; colX < maxCols; colX++) {
2092 data = row.SafeElementAt(colX);
2093 if (data) {
2094 // for an overlapping situation get the colspan from the originating cell and
2095 // use that as the max number of cols to iterate. Since this is rare, only
2096 // pay the price of looking up the cell's colspan here.
2097 if (!hitOverlap && data->IsOverlap()) {
2098 CellData* origData = row.SafeElementAt(aColIndex);
2099 if (origData && origData->IsOrig()) {
2100 nsTableCellFrame* cellFrame = origData->GetCellFrame();
2101 if (cellFrame) {
2102 // possible change the number of colums to iterate
2103 maxCols = PR_MIN(aColIndex + cellFrame->GetColSpan(), maxCols);
2104 if (colX >= maxCols)
2105 break;
2109 if (data->IsColSpan()) {
2110 colSpan++;
2111 if (data->IsZeroColSpan()) {
2112 aZeroColSpan = PR_TRUE;
2115 else {
2116 break;
2119 else break;
2121 return colSpan;
2124 PRInt32
2125 nsCellMap::GetRowSpanForNewCell(nsTableCellFrame* aCellFrameToAdd,
2126 PRInt32 aRowIndex,
2127 PRBool& aIsZeroRowSpan) const
2129 aIsZeroRowSpan = PR_FALSE;
2130 PRInt32 rowSpan = aCellFrameToAdd->GetRowSpan();
2131 if (0 == rowSpan) {
2132 // Use a min value of 2 for a zero rowspan to make computations easier
2133 // elsewhere. Zero rowspans are only content dependent!
2134 rowSpan = PR_MAX(2, mContentRowCount - aRowIndex);
2135 aIsZeroRowSpan = PR_TRUE;
2137 return rowSpan;
2140 PRBool nsCellMap::HasMoreThanOneCell(PRInt32 aRowIndex) const
2142 const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow);
2143 PRUint32 maxColIndex = row.Length();
2144 PRUint32 count = 0;
2145 PRUint32 colIndex;
2146 for (colIndex = 0; colIndex < maxColIndex; colIndex++) {
2147 CellData* cellData = row[colIndex];
2148 if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan()))
2149 count++;
2150 if (count > 1)
2151 return PR_TRUE;
2153 return PR_FALSE;
2156 PRInt32
2157 nsCellMap::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const
2159 const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow);
2160 PRUint32 count = 0;
2161 PRUint32 maxColIndex = row.Length();
2162 PRUint32 colIndex;
2163 for (colIndex = 0; colIndex < maxColIndex; colIndex++) {
2164 CellData* cellData = row[colIndex];
2165 if (cellData && cellData->IsOrig())
2166 count++;
2168 return count;
2171 PRInt32 nsCellMap::GetRowSpan(PRInt32 aRowIndex,
2172 PRInt32 aColIndex,
2173 PRBool aGetEffective) const
2175 PRInt32 rowSpan = 1;
2176 PRInt32 rowCount = (aGetEffective) ? mContentRowCount : mRows.Length();
2177 PRInt32 rowX;
2178 for (rowX = aRowIndex + 1; rowX < rowCount; rowX++) {
2179 CellData* data = GetDataAt(rowX, aColIndex);
2180 if (data) {
2181 if (data->IsRowSpan()) {
2182 rowSpan++;
2184 else {
2185 break;
2188 else break;
2190 return rowSpan;
2193 void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap,
2194 nsTableCellFrame& aCellFrame,
2195 PRInt32 aRowIndex,
2196 PRInt32 aColIndex,
2197 nsRect& aDamageArea)
2199 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2200 PRUint32 colX, rowX;
2202 // get the rowspan and colspan from the cell map since the content may have changed
2203 PRBool zeroColSpan;
2204 PRUint32 numCols = aMap.GetColCount();
2205 PRInt32 rowSpan = GetRowSpan(aRowIndex, aColIndex, PR_FALSE);
2206 PRUint32 colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan);
2207 PRUint32 endRowIndex = aRowIndex + rowSpan - 1;
2208 PRUint32 endColIndex = aColIndex + colSpan - 1;
2210 SetDamageArea(aColIndex, aRowIndex, 1 + endColIndex - aColIndex, 1 + endRowIndex - aRowIndex, aDamageArea);
2212 if (aMap.mTableFrame.HasZeroColSpans()) {
2213 aMap.mTableFrame.SetNeedColSpanExpansion(PR_TRUE);
2216 // adjust the col counts due to the deleted cell before removing it
2217 for (colX = aColIndex; colX <= endColIndex; colX++) {
2218 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2219 if (colX == PRUint32(aColIndex)) {
2220 colInfo->mNumCellsOrig--;
2222 else {
2223 colInfo->mNumCellsSpan--;
2227 // remove the deleted cell and cellData entries for it
2228 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
2229 CellDataArray& row = mRows[rowX];
2231 // endIndexForRow points at the first slot we don't want to clean up. This
2232 // makes the aColIndex == 0 case work right with our unsigned int colX.
2233 NS_ASSERTION(endColIndex + 1 <= row.Length(), "span beyond the row size!");
2234 PRUint32 endIndexForRow = PR_MIN(endColIndex + 1, row.Length());
2236 // Since endIndexForRow <= row.Length(), enough to compare aColIndex to it.
2237 if (PRUint32(aColIndex) < endIndexForRow) {
2238 for (colX = endIndexForRow; colX > PRUint32(aColIndex); colX--) {
2239 DestroyCellData(row[colX-1]);
2241 row.RemoveElementsAt(aColIndex, endIndexForRow - aColIndex);
2245 numCols = aMap.GetColCount();
2247 // update the row and col info due to shifting
2248 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
2249 CellDataArray& row = mRows[rowX];
2250 for (colX = aColIndex; colX < numCols - colSpan; colX++) {
2251 CellData* data = row.SafeElementAt(colX);
2252 if (data) {
2253 if (data->IsOrig()) {
2254 // a cell that gets moved to the left needs adjustment in its new location
2255 data->GetCellFrame()->SetColIndex(colX);
2256 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2257 colInfo->mNumCellsOrig++;
2258 // a cell that gets moved to the left needs adjustment in its old location
2259 colInfo = aMap.GetColInfoAt(colX + colSpan);
2260 if (colInfo) {
2261 colInfo->mNumCellsOrig--;
2265 else if (data->IsColSpan()) {
2266 // a cell that gets moved to the left needs adjustment
2267 // in its new location
2268 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2269 colInfo->mNumCellsSpan++;
2270 // a cell that gets moved to the left needs adjustment
2271 // in its old location
2272 colInfo = aMap.GetColInfoAt(colX + colSpan);
2273 if (colInfo) {
2274 colInfo->mNumCellsSpan--;
2280 aMap.RemoveColsAtEnd();
2283 void
2284 nsCellMap::RebuildConsideringRows(nsTableCellMap& aMap,
2285 PRInt32 aStartRowIndex,
2286 nsVoidArray* aRowsToInsert,
2287 PRInt32 aNumRowsToRemove,
2288 nsRect& aDamageArea)
2290 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2291 // copy the old cell map into a new array
2292 PRUint32 numOrigRows = mRows.Length();
2293 nsTArray<CellDataArray> origRows;
2294 mRows.SwapElements(origRows);
2296 PRInt32 rowNumberChange;
2297 if (aRowsToInsert) {
2298 rowNumberChange = aRowsToInsert->Count();
2299 } else {
2300 rowNumberChange = -aNumRowsToRemove;
2303 // adjust mContentRowCount based on the function arguments as they are known to
2304 // be real rows.
2305 mContentRowCount += rowNumberChange;
2306 NS_ASSERTION(mContentRowCount >= 0, "previous mContentRowCount was wrong");
2307 // mRows is empty now. Grow it to the size we expect it to have.
2308 if (mContentRowCount) {
2309 if (!Grow(aMap, mContentRowCount)) {
2310 // Bail, I guess... Not sure what else we can do here.
2311 return;
2315 // aStartRowIndex might be after all existing rows so we should limit the
2316 // copy to the amount of exisiting rows
2317 PRUint32 copyEndRowIndex = PR_MIN(numOrigRows, PRUint32(aStartRowIndex));
2319 // rowX keeps track of where we are in mRows while setting up the
2320 // new cellmap.
2321 PRUint32 rowX = 0;
2323 // put back the rows before the affected ones just as before. Note that we
2324 // can't just copy the old rows in bit-for-bit, because they might be
2325 // spanning out into the rows we're adding/removing.
2326 for ( ; rowX < copyEndRowIndex; rowX++) {
2327 const CellDataArray& row = origRows[rowX];
2328 PRUint32 numCols = row.Length();
2329 for (PRUint32 colX = 0; colX < numCols; colX++) {
2330 // put in the original cell from the cell map
2331 const CellData* data = row.ElementAt(colX);
2332 if (data && data->IsOrig()) {
2333 AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea);
2338 // Now handle the new rows being inserted, if any.
2339 PRUint32 copyStartRowIndex;
2340 rowX = aStartRowIndex;
2341 if (aRowsToInsert) {
2342 // add in the new cells and create rows if necessary
2343 PRInt32 numNewRows = aRowsToInsert->Count();
2344 for (PRInt32 newRowX = 0; newRowX < numNewRows; newRowX++) {
2345 nsTableRowFrame* rFrame = (nsTableRowFrame *)aRowsToInsert->ElementAt(newRowX);
2346 nsIFrame* cFrame = rFrame->GetFirstChild(nsnull);
2347 while (cFrame) {
2348 if (IS_TABLE_CELL(cFrame->GetType())) {
2349 AppendCell(aMap, (nsTableCellFrame *)cFrame, rowX, PR_FALSE, aDamageArea);
2351 cFrame = cFrame->GetNextSibling();
2353 rowX++;
2355 copyStartRowIndex = aStartRowIndex;
2357 else {
2358 copyStartRowIndex = aStartRowIndex + aNumRowsToRemove;
2361 // put back the rows after the affected ones just as before. Again, we can't
2362 // just copy the old bits because that would not handle the new rows spanning
2363 // out or our earlier old rows spanning through the damaged area.
2364 for (PRUint32 copyRowX = copyStartRowIndex; copyRowX < numOrigRows;
2365 copyRowX++) {
2366 const CellDataArray& row = origRows[copyRowX];
2367 PRUint32 numCols = row.Length();
2368 for (PRUint32 colX = 0; colX < numCols; colX++) {
2369 // put in the original cell from the cell map
2370 CellData* data = row.ElementAt(colX);
2371 if (data && data->IsOrig()) {
2372 AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea);
2375 rowX++;
2378 // delete the old cell map. Now rowX no longer has anything to do with mRows
2379 for (rowX = 0; rowX < numOrigRows; rowX++) {
2380 CellDataArray& row = origRows[rowX];
2381 PRUint32 len = row.Length();
2382 for (PRUint32 colX = 0; colX < len; colX++) {
2383 DestroyCellData(row[colX]);
2387 SetDamageArea(0, 0, aMap.GetColCount(), GetRowCount(), aDamageArea);
2390 void nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap,
2391 PRInt32 aNumOrigCols,
2392 nsVoidArray* aCellFrames,
2393 PRInt32 aRowIndex,
2394 PRInt32 aColIndex,
2395 PRBool aInsert,
2396 nsRect& aDamageArea)
2398 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2399 // copy the old cell map into a new array
2400 PRUint32 numOrigRows = mRows.Length();
2401 nsTArray<CellDataArray> origRows;
2402 mRows.SwapElements(origRows);
2404 PRInt32 numNewCells = (aCellFrames) ? aCellFrames->Count() : 0;
2406 // the new cells might extend the previous column number
2407 NS_ASSERTION(aNumOrigCols >= aColIndex, "Appending cells far beyond cellmap data?!");
2408 PRInt32 numCols = aInsert ? PR_MAX(aNumOrigCols, aColIndex + 1) : aNumOrigCols;
2410 // build the new cell map. Hard to say what, if anything, we can preallocate
2411 // here... Should come back to that sometime, perhaps.
2412 PRUint32 rowX;
2413 for (rowX = 0; rowX < numOrigRows; rowX++) {
2414 const CellDataArray& row = origRows[rowX];
2415 for (PRInt32 colX = 0; colX < numCols; colX++) {
2416 if ((rowX == aRowIndex) && (colX == aColIndex)) {
2417 if (aInsert) { // put in the new cells
2418 for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) {
2419 nsTableCellFrame* cell = (nsTableCellFrame*)aCellFrames->ElementAt(cellX);
2420 if (cell) {
2421 AppendCell(aMap, cell, rowX, PR_FALSE, aDamageArea);
2425 else {
2426 continue; // do not put the deleted cell back
2429 // put in the original cell from the cell map
2430 CellData* data = row.SafeElementAt(colX);
2431 if (data && data->IsOrig()) {
2432 AppendCell(aMap, data->GetCellFrame(), rowX, PR_FALSE, aDamageArea);
2436 if (aInsert && numOrigRows <= aRowIndex) { // append the new cells below the last original row
2437 NS_ASSERTION (numOrigRows == aRowIndex, "Appending cells far beyond the last row");
2438 for (PRInt32 cellX = 0; cellX < numNewCells; cellX++) {
2439 nsTableCellFrame* cell = (nsTableCellFrame*)aCellFrames->ElementAt(cellX);
2440 if (cell) {
2441 AppendCell(aMap, cell, aRowIndex, PR_FALSE, aDamageArea);
2446 // delete the old cell map
2447 for (rowX = 0; rowX < numOrigRows; rowX++) {
2448 CellDataArray& row = origRows[rowX];
2449 PRUint32 len = row.Length();
2450 for (PRUint32 colX = 0; colX < len; colX++) {
2451 DestroyCellData(row.SafeElementAt(colX));
2454 // expand the cellmap to cover empty content rows
2455 if (mRows.Length() < mContentRowCount) {
2456 Grow(aMap, mContentRowCount - mRows.Length());
2461 void nsCellMap::RemoveCell(nsTableCellMap& aMap,
2462 nsTableCellFrame* aCellFrame,
2463 PRInt32 aRowIndex,
2464 nsRect& aDamageArea)
2466 PRUint32 numRows = mRows.Length();
2467 if (PRUint32(aRowIndex) >= numRows) {
2468 NS_ERROR("bad arg in nsCellMap::RemoveCell");
2469 return;
2471 PRInt32 numCols = aMap.GetColCount();
2473 // Now aRowIndex is guaranteed OK.
2475 // get the starting col index of the cell to remove
2476 PRInt32 startColIndex;
2477 for (startColIndex = 0; startColIndex < numCols; startColIndex++) {
2478 CellData* data = mRows[aRowIndex].SafeElementAt(startColIndex);
2479 if (data && (data->IsOrig()) && (aCellFrame == data->GetCellFrame())) {
2480 break; // we found the col index
2484 PRInt32 rowSpan = GetRowSpan(aRowIndex, startColIndex, PR_FALSE);
2485 // record whether removing the cells is going to cause complications due
2486 // to existing row spans, col spans or table sizing.
2487 PRBool spansCauseRebuild = CellsSpanInOrOut(aRowIndex,
2488 aRowIndex + rowSpan - 1,
2489 startColIndex, numCols - 1);
2490 // XXX if the cell has a col span to the end of the map, and the end has no originating
2491 // cells, we need to assume that this the only such cell, and rebuild so that there are
2492 // no extraneous cols at the end. The same is true for removing rows.
2493 if (!aCellFrame->GetRowSpan() || !aCellFrame->GetColSpan())
2494 spansCauseRebuild = PR_TRUE;
2496 if (spansCauseRebuild) {
2497 aMap.RebuildConsideringCells(this, nsnull, aRowIndex, startColIndex, PR_FALSE, aDamageArea);
2499 else {
2500 ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex, aDamageArea);
2504 void nsCellMap::ExpandZeroColSpans(nsTableCellMap& aMap)
2506 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2507 PRUint32 numRows = mRows.Length();
2508 PRUint32 numCols = aMap.GetColCount();
2509 PRUint32 rowIndex, colIndex;
2511 for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
2512 for (colIndex = 0; colIndex < numCols; colIndex++) {
2513 CellData* data = mRows[rowIndex].SafeElementAt(colIndex);
2514 if (!data || !data->IsOrig())
2515 continue;
2516 nsTableCellFrame* cell = data->GetCellFrame();
2517 NS_ASSERTION(cell, "There has to be a cell");
2518 PRInt32 cellRowSpan = cell->GetRowSpan();
2519 PRInt32 cellColSpan = cell->GetColSpan();
2520 PRBool rowZeroSpan = (0 == cell->GetRowSpan());
2521 PRBool colZeroSpan = (0 == cell->GetColSpan());
2522 if (colZeroSpan) {
2523 aMap.mTableFrame.SetHasZeroColSpans(PR_TRUE);
2524 // do the expansion
2525 NS_ASSERTION(numRows > 0, "Bogus numRows");
2526 NS_ASSERTION(numCols > 0, "Bogus numCols");
2527 PRUint32 endRowIndex = rowZeroSpan ? numRows - 1 :
2528 rowIndex + cellRowSpan - 1;
2529 PRUint32 endColIndex = colZeroSpan ? numCols - 1 :
2530 colIndex + cellColSpan - 1;
2531 PRUint32 colX, rowX;
2532 colX = colIndex + 1;
2533 while (colX <= endColIndex) {
2534 // look at columns from here to our colspan. For each one, check
2535 // the rows from here to our rowspan to make sure there is no
2536 // obstacle to marking that column as a zerospanned column; if there
2537 // isn't, mark it so
2538 for (rowX = rowIndex; rowX <= endRowIndex; rowX++) {
2539 CellData* oldData = GetDataAt(rowX, colX);
2540 if (oldData) {
2541 if (oldData->IsOrig()) {
2542 break; // something is in the way
2544 if (oldData->IsRowSpan()) {
2545 if ((rowX - rowIndex) != oldData->GetRowSpanOffset()) {
2546 break;
2549 if (oldData->IsColSpan()) {
2550 if ((colX - colIndex) != oldData->GetColSpanOffset()) {
2551 break;
2556 if (endRowIndex >= rowX)
2557 break;// we hit something
2558 for (rowX = rowIndex; rowX <= endRowIndex; rowX++) {
2559 CellData* newData = AllocCellData(nsnull);
2560 if (!newData) return;
2562 newData->SetColSpanOffset(colX - colIndex);
2563 newData->SetZeroColSpan(PR_TRUE);
2565 if (rowX > rowIndex) {
2566 newData->SetRowSpanOffset(rowX - rowIndex);
2567 if (rowZeroSpan)
2568 newData->SetZeroRowSpan(PR_TRUE);
2570 SetDataAt(aMap, *newData, rowX, colX);
2572 colX++;
2573 } // while (colX <= endColIndex)
2574 } // if zerocolspan
2578 #ifdef NS_DEBUG
2579 void nsCellMap::Dump(PRBool aIsBorderCollapse) const
2581 printf("\n ***** START GROUP CELL MAP DUMP ***** %p\n", (void*)this);
2582 nsTableRowGroupFrame* rg = GetRowGroup();
2583 const nsStyleDisplay* display = rg->GetStyleDisplay();
2584 switch (display->mDisplay) {
2585 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
2586 printf(" thead ");
2587 break;
2588 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
2589 printf(" tfoot ");
2590 break;
2591 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
2592 printf(" tbody ");
2593 break;
2594 default:
2595 printf("HUH? wrong display type on rowgroup");
2597 PRUint32 mapRowCount = mRows.Length();
2598 printf("mapRowCount=%u tableRowCount=%d\n", mapRowCount, mContentRowCount);
2601 PRUint32 rowIndex, colIndex;
2602 for (rowIndex = 0; rowIndex < mapRowCount; rowIndex++) {
2603 const CellDataArray& row = mRows[rowIndex];
2604 printf(" row %d : ", rowIndex);
2605 PRUint32 colCount = row.Length();
2606 for (colIndex = 0; colIndex < colCount; colIndex++) {
2607 CellData* cd = row[colIndex];
2608 if (cd) {
2609 if (cd->IsOrig()) {
2610 printf("C%d,%d ", rowIndex, colIndex);
2611 } else {
2612 nsTableCellFrame* cell = nsnull;
2613 if (cd->IsRowSpan()) {
2614 cell = GetCellFrame(rowIndex, colIndex, *cd, PR_TRUE);
2615 printf("R ");
2617 if (cd->IsColSpan()) {
2618 cell = GetCellFrame(rowIndex, colIndex, *cd, PR_FALSE);
2619 printf("C ");
2621 if (!(cd->IsRowSpan() && cd->IsColSpan())) {
2622 printf(" ");
2624 printf(" ");
2626 } else {
2627 printf("---- ");
2630 if (aIsBorderCollapse) {
2631 nscoord size;
2632 BCBorderOwner owner;
2633 PRUint8 side;
2634 PRBool segStart;
2635 PRPackedBool bevel;
2636 for (PRInt32 i = 0; i <= 2; i++) {
2637 printf("\n ");
2638 for (colIndex = 0; colIndex < colCount; colIndex++) {
2639 BCCellData* cd = (BCCellData *)row[colIndex];
2640 if (cd) {
2641 if (0 == i) {
2642 size = cd->mData.GetTopEdge(owner, segStart);
2643 printf("t=%d%d%d ", size, owner, segStart);
2645 else if (1 == i) {
2646 size = cd->mData.GetLeftEdge(owner, segStart);
2647 printf("l=%d%d%d ", size, owner, segStart);
2649 else {
2650 size = cd->mData.GetCorner(side, bevel);
2651 printf("c=%d%d%d ", size, side, bevel);
2657 printf("\n");
2660 // output info mapping Ci,j to cell address
2661 PRUint32 cellCount = 0;
2662 for (PRUint32 rIndex = 0; rIndex < mapRowCount; rIndex++) {
2663 const CellDataArray& row = mRows[rIndex];
2664 PRUint32 colCount = row.Length();
2665 printf(" ");
2666 for (colIndex = 0; colIndex < colCount; colIndex++) {
2667 CellData* cd = row[colIndex];
2668 if (cd) {
2669 if (cd->IsOrig()) {
2670 nsTableCellFrame* cellFrame = cd->GetCellFrame();
2671 PRInt32 cellFrameColIndex;
2672 cellFrame->GetColIndex(cellFrameColIndex);
2673 printf("C%d,%d=%p(%d) ", rIndex, colIndex, (void*)cellFrame,
2674 cellFrameColIndex);
2675 cellCount++;
2679 printf("\n");
2682 printf(" ***** END GROUP CELL MAP DUMP *****\n");
2684 #endif
2686 PRBool
2687 nsCellMap::IsZeroColSpan(PRInt32 aRowIndex,
2688 PRInt32 aColIndex) const
2690 CellData* data =
2691 mRows.SafeElementAt(aRowIndex, *sEmptyRow).SafeElementAt(aColIndex);
2692 return data && data->IsZeroColSpan();
2695 CellData*
2696 nsCellMap::GetDataAt(PRInt32 aMapRowIndex,
2697 PRInt32 aColIndex) const
2699 return
2700 mRows.SafeElementAt(aMapRowIndex, *sEmptyRow).SafeElementAt(aColIndex);
2703 // only called if the cell at aMapRowIndex, aColIndex is null or dead
2704 // (the latter from ExpandZeroColSpans).
2705 void nsCellMap::SetDataAt(nsTableCellMap& aMap,
2706 CellData& aNewCell,
2707 PRInt32 aMapRowIndex,
2708 PRInt32 aColIndex)
2710 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2711 if (PRUint32(aMapRowIndex) >= mRows.Length()) {
2712 NS_ERROR("SetDataAt called with row index > num rows");
2713 return;
2716 CellDataArray& row = mRows[aMapRowIndex];
2718 // the table map may need cols added
2719 PRInt32 numColsToAdd = aColIndex + 1 - aMap.GetColCount();
2720 if (numColsToAdd > 0) {
2721 aMap.AddColsAtEnd(numColsToAdd);
2723 // the row may need cols added
2724 numColsToAdd = aColIndex + 1 - row.Length();
2725 if (numColsToAdd > 0) {
2726 // XXXbz need to handle allocation failures.
2727 GrowRow(row, numColsToAdd);
2730 DestroyCellData(row[aColIndex]);
2732 row.ReplaceElementsAt(aColIndex, 1, &aNewCell);
2733 // update the originating cell counts if cell originates in this row, col
2734 nsColInfo* colInfo = aMap.GetColInfoAt(aColIndex);
2735 if (colInfo) {
2736 if (aNewCell.IsOrig()) {
2737 colInfo->mNumCellsOrig++;
2739 else if (aNewCell.IsColSpan()) {
2740 colInfo->mNumCellsSpan++;
2743 else NS_ERROR("SetDataAt called with col index > table map num cols");
2746 nsTableCellFrame*
2747 nsCellMap::GetCellInfoAt(const nsTableCellMap& aMap,
2748 PRInt32 aRowX,
2749 PRInt32 aColX,
2750 PRBool* aOriginates,
2751 PRInt32* aColSpan) const
2753 if (aOriginates) {
2754 *aOriginates = PR_FALSE;
2756 CellData* data = GetDataAt(aRowX, aColX);
2757 nsTableCellFrame* cellFrame = nsnull;
2758 if (data) {
2759 if (data->IsOrig()) {
2760 cellFrame = data->GetCellFrame();
2761 if (aOriginates)
2762 *aOriginates = PR_TRUE;
2764 else {
2765 cellFrame = GetCellFrame(aRowX, aColX, *data, PR_TRUE);
2767 if (cellFrame && aColSpan) {
2768 PRInt32 initialColIndex;
2769 cellFrame->GetColIndex(initialColIndex);
2770 PRBool zeroSpan;
2771 *aColSpan = GetEffectiveColSpan(aMap, aRowX, initialColIndex, zeroSpan);
2774 return cellFrame;
2778 PRBool nsCellMap::RowIsSpannedInto(PRInt32 aRowIndex,
2779 PRInt32 aNumEffCols) const
2781 if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) {
2782 return PR_FALSE;
2784 for (PRInt32 colIndex = 0; colIndex < aNumEffCols; colIndex++) {
2785 CellData* cd = GetDataAt(aRowIndex, colIndex);
2786 if (cd) { // there's really a cell at (aRowIndex, colIndex)
2787 if (cd->IsSpan()) { // the cell at (aRowIndex, colIndex) is the result of a span
2788 if (cd->IsRowSpan() && GetCellFrame(aRowIndex, colIndex, *cd, PR_TRUE)) { // XXX why the last check
2789 return PR_TRUE;
2794 return PR_FALSE;
2797 PRBool nsCellMap::RowHasSpanningCells(PRInt32 aRowIndex,
2798 PRInt32 aNumEffCols) const
2800 if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) {
2801 return PR_FALSE;
2803 if (aRowIndex != mContentRowCount - 1) {
2804 // aRowIndex is not the last row, so we check the next row after aRowIndex for spanners
2805 for (PRInt32 colIndex = 0; colIndex < aNumEffCols; colIndex++) {
2806 CellData* cd = GetDataAt(aRowIndex, colIndex);
2807 if (cd && (cd->IsOrig())) { // cell originates
2808 CellData* cd2 = GetDataAt(aRowIndex + 1, colIndex);
2809 if (cd2 && cd2->IsRowSpan()) { // cd2 is spanned by a row
2810 if (cd->GetCellFrame() == GetCellFrame(aRowIndex + 1, colIndex, *cd2, PR_TRUE)) {
2811 return PR_TRUE;
2817 return PR_FALSE;
2820 PRBool nsCellMap::ColHasSpanningCells(PRInt32 aColIndex) const
2822 for (PRInt32 rowIndex = 0; rowIndex < mContentRowCount; rowIndex++) {
2823 CellData* cd = GetDataAt(rowIndex, aColIndex);
2824 if (cd && (cd->IsOrig())) { // cell originates
2825 CellData* cd2 = GetDataAt(rowIndex, aColIndex +1);
2826 if (cd2 && cd2->IsColSpan()) { // cd2 is spanned by a col
2827 if (cd->GetCellFrame() == GetCellFrame(rowIndex , aColIndex + 1, *cd2, PR_FALSE)) {
2828 return PR_TRUE;
2833 return PR_FALSE;
2836 void nsCellMap::DestroyCellData(CellData* aData)
2838 if (!aData) {
2839 return;
2842 if (mIsBC) {
2843 BCCellData* bcData = static_cast<BCCellData*>(aData);
2844 bcData->~BCCellData();
2845 mPresContext->FreeToShell(sizeof(BCCellData), bcData);
2846 } else {
2847 aData->~CellData();
2848 mPresContext->FreeToShell(sizeof(CellData), aData);
2852 CellData* nsCellMap::AllocCellData(nsTableCellFrame* aOrigCell)
2854 if (mIsBC) {
2855 BCCellData* data = (BCCellData*)
2856 mPresContext->AllocateFromShell(sizeof(BCCellData));
2857 if (data) {
2858 new (data) BCCellData(aOrigCell);
2860 return data;
2863 CellData* data = (CellData*)
2864 mPresContext->AllocateFromShell(sizeof(CellData));
2865 if (data) {
2866 new (data) CellData(aOrigCell);
2868 return data;
2871 void
2872 nsCellMapColumnIterator::AdvanceRowGroup()
2874 do {
2875 mCurMapStart += mCurMapContentRowCount;
2876 mCurMap = mCurMap->GetNextSibling();
2877 if (!mCurMap) {
2878 // Set mCurMapContentRowCount and mCurMapRelevantRowCount to 0 in case
2879 // mCurMap has no next sibling. This can happen if we just handled the
2880 // last originating cell. Future calls will end up with mFoundCells ==
2881 // mOrigCells, but for this one mFoundCells was definitely not big enough
2882 // if we got here.
2883 mCurMapContentRowCount = 0;
2884 mCurMapRelevantRowCount = 0;
2885 break;
2888 mCurMapContentRowCount = mCurMap->GetRowCount();
2889 PRUint32 rowArrayLength = mCurMap->mRows.Length();
2890 mCurMapRelevantRowCount = PR_MIN(mCurMapContentRowCount, rowArrayLength);
2891 } while (0 == mCurMapRelevantRowCount);
2893 NS_ASSERTION(mCurMapRelevantRowCount != 0 || !mCurMap,
2894 "How did that happen?");
2896 // Set mCurMapRow to 0, since cells can't span across table row groups.
2897 mCurMapRow = 0;
2900 void
2901 nsCellMapColumnIterator::IncrementRow(PRInt32 aIncrement)
2903 NS_PRECONDITION(aIncrement >= 0, "Bogus increment");
2904 NS_PRECONDITION(mCurMap, "Bogus mOrigCells?");
2905 if (aIncrement == 0) {
2906 AdvanceRowGroup();
2908 else {
2909 mCurMapRow += aIncrement;
2910 if (mCurMapRow >= mCurMapRelevantRowCount) {
2911 AdvanceRowGroup();
2916 nsTableCellFrame*
2917 nsCellMapColumnIterator::GetNextFrame(PRInt32* aRow, PRInt32* aColSpan)
2919 // Fast-path for the case when we don't have anything left in the column and
2920 // we know it.
2921 if (mFoundCells == mOrigCells) {
2922 *aRow = 0;
2923 *aColSpan = 1;
2924 return nsnull;
2927 while (1) {
2928 NS_ASSERTION(mCurMapRow < mCurMapRelevantRowCount, "Bogus mOrigCells?");
2929 // Safe to just get the row (which is faster than calling GetDataAt(), but
2930 // there may not be that many cells in it, so have to use SafeElementAt for
2931 // the mCol.
2932 const nsCellMap::CellDataArray& row = mCurMap->mRows[mCurMapRow];
2933 CellData* cellData = row.SafeElementAt(mCol);
2934 if (!cellData || cellData->IsDead()) {
2935 // Could hit this if there are fewer cells in this row than others, for
2936 // example.
2937 IncrementRow(1);
2938 continue;
2941 if (cellData->IsColSpan()) {
2942 // Look up the originating data for this cell, advance by its relative rowspan.
2943 PRInt32 rowspanOffset = cellData->GetRowSpanOffset();
2944 nsTableCellFrame* cellFrame = mCurMap->GetCellFrame(mCurMapRow, mCol, *cellData, PR_FALSE);
2945 NS_ASSERTION(cellFrame,"Must have usable originating data here");
2946 PRInt32 rowSpan = cellFrame->GetRowSpan();
2947 if (rowSpan == 0) {
2948 AdvanceRowGroup();
2950 else {
2951 IncrementRow(rowSpan - rowspanOffset);
2953 continue;
2956 NS_ASSERTION(cellData->IsOrig(),
2957 "Must have originating cellData by this point. "
2958 "See comment on mCurMapRow in header.");
2960 nsTableCellFrame* cellFrame = cellData->GetCellFrame();
2961 NS_ASSERTION(cellFrame, "Orig data without cellframe?");
2963 *aRow = mCurMapStart + mCurMapRow;
2964 PRBool ignoredZeroSpan;
2965 *aColSpan = mCurMap->GetEffectiveColSpan(*mMap, mCurMapRow, mCol,
2966 ignoredZeroSpan);
2968 IncrementRow(cellFrame->GetRowSpan());
2970 ++mFoundCells;
2972 NS_ASSERTION(cellData = mMap->GetDataAt(*aRow, mCol),
2973 "Giving caller bogus row?");
2975 return cellFrame;
2978 NS_NOTREACHED("Can't get here");
2979 return nsnull;