Fri, 16 Jan 2015 18:13:44 +0100
Integrate suggestion from review to improve consistency with existing code.
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsTArray.h"
7 #include "nsCellMap.h"
8 #include "nsTableFrame.h"
9 #include "nsTableCellFrame.h"
10 #include "nsTableRowFrame.h"
11 #include "nsTableRowGroupFrame.h"
12 #include <algorithm>
15 static void
16 SetDamageArea(int32_t aXOrigin,
17 int32_t aYOrigin,
18 int32_t aWidth,
19 int32_t aHeight,
20 nsIntRect& aDamageArea)
21 {
22 NS_ASSERTION(aXOrigin >= 0, "negative col index");
23 NS_ASSERTION(aYOrigin >= 0, "negative row index");
24 NS_ASSERTION(aWidth >= 0, "negative horizontal damage");
25 NS_ASSERTION(aHeight >= 0, "negative vertical damage");
26 aDamageArea.x = aXOrigin;
27 aDamageArea.y = aYOrigin;
28 aDamageArea.width = aWidth;
29 aDamageArea.height = aHeight;
30 }
32 // Empty static array used for SafeElementAt() calls on mRows.
33 static nsCellMap::CellDataArray * sEmptyRow;
35 // CellData
37 CellData::CellData(nsTableCellFrame* aOrigCell)
38 {
39 MOZ_COUNT_CTOR(CellData);
40 static_assert(sizeof(mOrigCell) == sizeof(mBits),
41 "mOrigCell and mBits must be the same size");
42 mOrigCell = aOrigCell;
43 }
45 CellData::~CellData()
46 {
47 MOZ_COUNT_DTOR(CellData);
48 }
50 BCCellData::BCCellData(nsTableCellFrame* aOrigCell)
51 :CellData(aOrigCell)
52 {
53 MOZ_COUNT_CTOR(BCCellData);
54 }
56 BCCellData::~BCCellData()
57 {
58 MOZ_COUNT_DTOR(BCCellData);
59 }
61 // nsTableCellMap
63 nsTableCellMap::nsTableCellMap(nsTableFrame& aTableFrame,
64 bool aBorderCollapse)
65 :mTableFrame(aTableFrame), mFirstMap(nullptr), mBCInfo(nullptr)
66 {
67 MOZ_COUNT_CTOR(nsTableCellMap);
69 nsTableFrame::RowGroupArray orderedRowGroups;
70 aTableFrame.OrderRowGroups(orderedRowGroups);
72 nsTableRowGroupFrame* prior = nullptr;
73 for (uint32_t rgX = 0; rgX < orderedRowGroups.Length(); rgX++) {
74 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX];
75 InsertGroupCellMap(rgFrame, prior);
76 prior = rgFrame;
77 }
78 if (aBorderCollapse) {
79 mBCInfo = new BCInfo();
80 }
81 }
83 nsTableCellMap::~nsTableCellMap()
84 {
85 MOZ_COUNT_DTOR(nsTableCellMap);
87 nsCellMap* cellMap = mFirstMap;
88 while (cellMap) {
89 nsCellMap* next = cellMap->GetNextSibling();
90 delete cellMap;
91 cellMap = next;
92 }
94 if (mBCInfo) {
95 DeleteRightBottomBorders();
96 delete mBCInfo;
97 }
98 }
100 // Get the bcData holding the border segments of the right edge of the table
101 BCData*
102 nsTableCellMap::GetRightMostBorder(int32_t aRowIndex)
103 {
104 if (!mBCInfo) ABORT1(nullptr);
106 int32_t numRows = mBCInfo->mRightBorders.Length();
107 if (aRowIndex < numRows) {
108 return &mBCInfo->mRightBorders.ElementAt(aRowIndex);
109 }
111 mBCInfo->mRightBorders.SetLength(aRowIndex+1);
112 return &mBCInfo->mRightBorders.ElementAt(aRowIndex);
113 }
115 // Get the bcData holding the border segments of the bottom edge of the table
116 BCData*
117 nsTableCellMap::GetBottomMostBorder(int32_t aColIndex)
118 {
119 if (!mBCInfo) ABORT1(nullptr);
121 int32_t numCols = mBCInfo->mBottomBorders.Length();
122 if (aColIndex < numCols) {
123 return &mBCInfo->mBottomBorders.ElementAt(aColIndex);
124 }
126 mBCInfo->mBottomBorders.SetLength(aColIndex+1);
127 return &mBCInfo->mBottomBorders.ElementAt(aColIndex);
128 }
130 // delete the borders corresponding to the right and bottom edges of the table
131 void
132 nsTableCellMap::DeleteRightBottomBorders()
133 {
134 if (mBCInfo) {
135 mBCInfo->mBottomBorders.Clear();
136 mBCInfo->mRightBorders.Clear();
137 }
138 }
140 void
141 nsTableCellMap::InsertGroupCellMap(nsCellMap* aPrevMap,
142 nsCellMap& aNewMap)
143 {
144 nsCellMap* next;
145 if (aPrevMap) {
146 next = aPrevMap->GetNextSibling();
147 aPrevMap->SetNextSibling(&aNewMap);
148 }
149 else {
150 next = mFirstMap;
151 mFirstMap = &aNewMap;
152 }
153 aNewMap.SetNextSibling(next);
154 }
156 void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame* aNewGroup,
157 nsTableRowGroupFrame*& aPrevGroup)
158 {
159 nsCellMap* newMap = new nsCellMap(aNewGroup, mBCInfo != nullptr);
160 nsCellMap* prevMap = nullptr;
161 nsCellMap* lastMap = mFirstMap;
162 if (aPrevGroup) {
163 nsCellMap* map = mFirstMap;
164 while (map) {
165 lastMap = map;
166 if (map->GetRowGroup() == aPrevGroup) {
167 prevMap = map;
168 break;
169 }
170 map = map->GetNextSibling();
171 }
172 }
173 if (!prevMap) {
174 if (aPrevGroup) {
175 prevMap = lastMap;
176 aPrevGroup = (prevMap) ? prevMap->GetRowGroup() : nullptr;
177 }
178 else {
179 aPrevGroup = nullptr;
180 }
181 }
182 InsertGroupCellMap(prevMap, *newMap);
183 }
185 void nsTableCellMap::RemoveGroupCellMap(nsTableRowGroupFrame* aGroup)
186 {
187 nsCellMap* map = mFirstMap;
188 nsCellMap* prior = nullptr;
189 while (map) {
190 if (map->GetRowGroup() == aGroup) {
191 nsCellMap* next = map->GetNextSibling();
192 if (mFirstMap == map) {
193 mFirstMap = next;
194 }
195 else {
196 prior->SetNextSibling(next);
197 }
198 delete map;
199 break;
200 }
201 prior = map;
202 map = map->GetNextSibling();
203 }
204 }
206 static nsCellMap*
207 FindMapFor(const nsTableRowGroupFrame* aRowGroup,
208 nsCellMap* aStart,
209 const nsCellMap* aEnd)
210 {
211 for (nsCellMap* map = aStart; map != aEnd; map = map->GetNextSibling()) {
212 if (aRowGroup == map->GetRowGroup()) {
213 return map;
214 }
215 }
217 return nullptr;
218 }
220 nsCellMap*
221 nsTableCellMap::GetMapFor(const nsTableRowGroupFrame* aRowGroup,
222 nsCellMap* aStartHint) const
223 {
224 NS_PRECONDITION(aRowGroup, "Must have a rowgroup");
225 NS_ASSERTION(!aRowGroup->GetPrevInFlow(), "GetMapFor called with continuation");
226 if (aStartHint) {
227 nsCellMap* map = FindMapFor(aRowGroup, aStartHint, nullptr);
228 if (map) {
229 return map;
230 }
231 }
233 nsCellMap* map = FindMapFor(aRowGroup, mFirstMap, aStartHint);
234 if (map) {
235 return map;
236 }
238 // if aRowGroup is a repeated header or footer find the header or footer it was repeated from
239 if (aRowGroup->IsRepeatable()) {
240 nsTableFrame* fifTable = static_cast<nsTableFrame*>(mTableFrame.FirstInFlow());
242 const nsStyleDisplay* display = aRowGroup->StyleDisplay();
243 nsTableRowGroupFrame* rgOrig =
244 (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay) ?
245 fifTable->GetTHead() : fifTable->GetTFoot();
246 // find the row group cell map using the original header/footer
247 if (rgOrig && rgOrig != aRowGroup) {
248 return GetMapFor(rgOrig, aStartHint);
249 }
250 }
252 return nullptr;
253 }
255 void
256 nsTableCellMap::Synchronize(nsTableFrame* aTableFrame)
257 {
258 nsTableFrame::RowGroupArray orderedRowGroups;
259 nsAutoTArray<nsCellMap*, 8> maps;
261 aTableFrame->OrderRowGroups(orderedRowGroups);
262 if (!orderedRowGroups.Length()) {
263 return;
264 }
266 // XXXbz this fails if orderedRowGroups is missing some row groups
267 // (due to OOM when appending to the array, e.g. -- we leak maps in
268 // that case).
270 // Scope |map| outside the loop so we can use it as a hint.
271 nsCellMap* map = nullptr;
272 for (uint32_t rgX = 0; rgX < orderedRowGroups.Length(); rgX++) {
273 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX];
274 map = GetMapFor(static_cast<nsTableRowGroupFrame*>(rgFrame->FirstInFlow()),
275 map);
276 if (map) {
277 if (!maps.AppendElement(map)) {
278 delete map;
279 map = nullptr;
280 NS_WARNING("Could not AppendElement");
281 break;
282 }
283 }
284 }
285 if (maps.IsEmpty()) {
286 MOZ_ASSERT(!mFirstMap);
287 return;
288 }
290 int32_t mapIndex = maps.Length() - 1; // Might end up -1
291 nsCellMap* nextMap = maps.ElementAt(mapIndex);
292 nextMap->SetNextSibling(nullptr);
293 for (mapIndex-- ; mapIndex >= 0; mapIndex--) {
294 nsCellMap* map = maps.ElementAt(mapIndex);
295 map->SetNextSibling(nextMap);
296 nextMap = map;
297 }
298 mFirstMap = nextMap;
299 }
301 bool
302 nsTableCellMap::HasMoreThanOneCell(int32_t aRowIndex) const
303 {
304 int32_t rowIndex = aRowIndex;
305 nsCellMap* map = mFirstMap;
306 while (map) {
307 if (map->GetRowCount() > rowIndex) {
308 return map->HasMoreThanOneCell(rowIndex);
309 }
310 rowIndex -= map->GetRowCount();
311 map = map->GetNextSibling();
312 }
313 return false;
314 }
316 int32_t
317 nsTableCellMap::GetNumCellsOriginatingInRow(int32_t aRowIndex) const
318 {
319 int32_t rowIndex = aRowIndex;
320 nsCellMap* map = mFirstMap;
321 while (map) {
322 if (map->GetRowCount() > rowIndex) {
323 return map->GetNumCellsOriginatingInRow(rowIndex);
324 }
325 rowIndex -= map->GetRowCount();
326 map = map->GetNextSibling();
327 }
328 return 0;
329 }
330 int32_t
331 nsTableCellMap::GetEffectiveRowSpan(int32_t aRowIndex,
332 int32_t aColIndex) const
333 {
334 int32_t rowIndex = aRowIndex;
335 nsCellMap* map = mFirstMap;
336 while (map) {
337 if (map->GetRowCount() > rowIndex) {
338 return map->GetRowSpan(rowIndex, aColIndex, true);
339 }
340 rowIndex -= map->GetRowCount();
341 map = map->GetNextSibling();
342 }
343 NS_NOTREACHED("Bogus row index?");
344 return 0;
345 }
347 int32_t
348 nsTableCellMap::GetEffectiveColSpan(int32_t aRowIndex,
349 int32_t aColIndex) const
350 {
351 int32_t rowIndex = aRowIndex;
352 nsCellMap* map = mFirstMap;
353 while (map) {
354 if (map->GetRowCount() > rowIndex) {
355 bool zeroColSpan;
356 return map->GetEffectiveColSpan(*this, rowIndex, aColIndex, zeroColSpan);
357 }
358 rowIndex -= map->GetRowCount();
359 map = map->GetNextSibling();
360 }
361 NS_NOTREACHED("Bogus row index?");
362 return 0;
363 }
365 nsTableCellFrame*
366 nsTableCellMap::GetCellFrame(int32_t aRowIndex,
367 int32_t aColIndex,
368 CellData& aData,
369 bool aUseRowIfOverlap) const
370 {
371 int32_t rowIndex = aRowIndex;
372 nsCellMap* map = mFirstMap;
373 while (map) {
374 if (map->GetRowCount() > rowIndex) {
375 return map->GetCellFrame(rowIndex, aColIndex, aData, aUseRowIfOverlap);
376 }
377 rowIndex -= map->GetRowCount();
378 map = map->GetNextSibling();
379 }
380 return nullptr;
381 }
383 nsColInfo*
384 nsTableCellMap::GetColInfoAt(int32_t aColIndex)
385 {
386 int32_t numColsToAdd = aColIndex + 1 - mCols.Length();
387 if (numColsToAdd > 0) {
388 AddColsAtEnd(numColsToAdd); // XXX this could fail to add cols in theory
389 }
390 return &mCols.ElementAt(aColIndex);
391 }
393 int32_t
394 nsTableCellMap::GetRowCount() const
395 {
396 int32_t numRows = 0;
397 nsCellMap* map = mFirstMap;
398 while (map) {
399 numRows += map->GetRowCount();
400 map = map->GetNextSibling();
401 }
402 return numRows;
403 }
405 CellData*
406 nsTableCellMap::GetDataAt(int32_t aRowIndex,
407 int32_t aColIndex) const
408 {
409 int32_t rowIndex = aRowIndex;
410 nsCellMap* map = mFirstMap;
411 while (map) {
412 if (map->GetRowCount() > rowIndex) {
413 return map->GetDataAt(rowIndex, aColIndex);
414 }
415 rowIndex -= map->GetRowCount();
416 map = map->GetNextSibling();
417 }
418 return nullptr;
419 }
421 void
422 nsTableCellMap::AddColsAtEnd(uint32_t aNumCols)
423 {
424 if (!mCols.AppendElements(aNumCols)) {
425 NS_WARNING("Could not AppendElement");
426 }
427 if (mBCInfo) {
428 if (!mBCInfo->mBottomBorders.AppendElements(aNumCols)) {
429 NS_WARNING("Could not AppendElement");
430 }
431 }
432 }
434 void
435 nsTableCellMap::RemoveColsAtEnd()
436 {
437 // Remove the cols at the end which don't have originating cells or cells spanning
438 // into them. Only do this if the col was created as eColAnonymousCell
439 int32_t numCols = GetColCount();
440 int32_t lastGoodColIndex = mTableFrame.GetIndexOfLastRealCol();
441 for (int32_t colX = numCols - 1; (colX >= 0) && (colX > lastGoodColIndex); colX--) {
442 nsColInfo& colInfo = mCols.ElementAt(colX);
443 if ((colInfo.mNumCellsOrig <= 0) && (colInfo.mNumCellsSpan <= 0)) {
444 mCols.RemoveElementAt(colX);
446 if (mBCInfo) {
447 int32_t count = mBCInfo->mBottomBorders.Length();
448 if (colX < count) {
449 mBCInfo->mBottomBorders.RemoveElementAt(colX);
450 }
451 }
452 }
453 else break; // only remove until we encounter the 1st valid one
454 }
455 }
457 void
458 nsTableCellMap::ClearCols()
459 {
460 mCols.Clear();
461 if (mBCInfo)
462 mBCInfo->mBottomBorders.Clear();
463 }
464 void
465 nsTableCellMap::InsertRows(nsTableRowGroupFrame* aParent,
466 nsTArray<nsTableRowFrame*>& aRows,
467 int32_t aFirstRowIndex,
468 bool aConsiderSpans,
469 nsIntRect& aDamageArea)
470 {
471 int32_t numNewRows = aRows.Length();
472 if ((numNewRows <= 0) || (aFirstRowIndex < 0)) ABORT0();
474 int32_t rowIndex = aFirstRowIndex;
475 int32_t rgStartRowIndex = 0;
476 nsCellMap* cellMap = mFirstMap;
477 while (cellMap) {
478 nsTableRowGroupFrame* rg = cellMap->GetRowGroup();
479 if (rg == aParent) {
480 cellMap->InsertRows(*this, aRows, rowIndex, aConsiderSpans,
481 rgStartRowIndex, aDamageArea);
482 #ifdef DEBUG_TABLE_CELLMAP
483 Dump("after InsertRows");
484 #endif
485 if (mBCInfo) {
486 int32_t count = mBCInfo->mRightBorders.Length();
487 if (aFirstRowIndex < count) {
488 for (int32_t rowX = aFirstRowIndex; rowX < aFirstRowIndex + numNewRows; rowX++) {
489 mBCInfo->mRightBorders.InsertElementAt(rowX);
490 }
491 }
492 else {
493 GetRightMostBorder(aFirstRowIndex); // this will create missing entries
494 for (int32_t rowX = aFirstRowIndex + 1; rowX < aFirstRowIndex + numNewRows; rowX++) {
495 mBCInfo->mRightBorders.AppendElement();
496 }
497 }
498 }
499 return;
500 }
501 int32_t rowCount = cellMap->GetRowCount();
502 rgStartRowIndex += rowCount;
503 rowIndex -= rowCount;
504 cellMap = cellMap->GetNextSibling();
505 }
507 NS_ERROR("Attempt to insert row into wrong map.");
508 }
510 void
511 nsTableCellMap::RemoveRows(int32_t aFirstRowIndex,
512 int32_t aNumRowsToRemove,
513 bool aConsiderSpans,
514 nsIntRect& aDamageArea)
515 {
516 int32_t rowIndex = aFirstRowIndex;
517 int32_t rgStartRowIndex = 0;
518 nsCellMap* cellMap = mFirstMap;
519 while (cellMap) {
520 int32_t rowCount = cellMap->GetRowCount();
521 if (rowCount > rowIndex) {
522 cellMap->RemoveRows(*this, rowIndex, aNumRowsToRemove, aConsiderSpans,
523 rgStartRowIndex, aDamageArea);
524 if (mBCInfo) {
525 for (int32_t rowX = aFirstRowIndex + aNumRowsToRemove - 1; rowX >= aFirstRowIndex; rowX--) {
526 if (uint32_t(rowX) < mBCInfo->mRightBorders.Length()) {
527 mBCInfo->mRightBorders.RemoveElementAt(rowX);
528 }
529 }
530 }
531 break;
532 }
533 rgStartRowIndex += rowCount;
534 rowIndex -= rowCount;
535 cellMap = cellMap->GetNextSibling();
536 }
537 #ifdef DEBUG_TABLE_CELLMAP
538 Dump("after RemoveRows");
539 #endif
540 }
544 CellData*
545 nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame,
546 int32_t aRowIndex,
547 bool aRebuildIfNecessary,
548 nsIntRect& aDamageArea)
549 {
550 MOZ_ASSERT(&aCellFrame == aCellFrame.FirstInFlow(),
551 "invalid call on continuing frame");
552 nsIFrame* rgFrame = aCellFrame.GetParent(); // get the row
553 if (!rgFrame) return 0;
554 rgFrame = rgFrame->GetParent(); // get the row group
555 if (!rgFrame) return 0;
557 CellData* result = nullptr;
558 int32_t rowIndex = aRowIndex;
559 int32_t rgStartRowIndex = 0;
560 nsCellMap* cellMap = mFirstMap;
561 while (cellMap) {
562 if (cellMap->GetRowGroup() == rgFrame) {
563 result = cellMap->AppendCell(*this, &aCellFrame, rowIndex,
564 aRebuildIfNecessary, rgStartRowIndex,
565 aDamageArea);
566 break;
567 }
568 int32_t rowCount = cellMap->GetRowCount();
569 rgStartRowIndex += rowCount;
570 rowIndex -= rowCount;
571 cellMap = cellMap->GetNextSibling();
572 }
573 #ifdef DEBUG_TABLE_CELLMAP
574 Dump("after AppendCell");
575 #endif
576 return result;
577 }
580 void
581 nsTableCellMap::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames,
582 int32_t aRowIndex,
583 int32_t aColIndexBefore,
584 nsIntRect& aDamageArea)
585 {
586 int32_t rowIndex = aRowIndex;
587 int32_t rgStartRowIndex = 0;
588 nsCellMap* cellMap = mFirstMap;
589 while (cellMap) {
590 int32_t rowCount = cellMap->GetRowCount();
591 if (rowCount > rowIndex) {
592 cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore,
593 rgStartRowIndex, aDamageArea);
594 break;
595 }
596 rgStartRowIndex += rowCount;
597 rowIndex -= rowCount;
598 cellMap = cellMap->GetNextSibling();
599 }
600 #ifdef DEBUG_TABLE_CELLMAP
601 Dump("after InsertCells");
602 #endif
603 }
606 void
607 nsTableCellMap::RemoveCell(nsTableCellFrame* aCellFrame,
608 int32_t aRowIndex,
609 nsIntRect& aDamageArea)
610 {
611 if (!aCellFrame) ABORT0();
612 MOZ_ASSERT(aCellFrame == aCellFrame->FirstInFlow(),
613 "invalid call on continuing frame");
614 int32_t rowIndex = aRowIndex;
615 int32_t rgStartRowIndex = 0;
616 nsCellMap* cellMap = mFirstMap;
617 while (cellMap) {
618 int32_t rowCount = cellMap->GetRowCount();
619 if (rowCount > rowIndex) {
620 cellMap->RemoveCell(*this, aCellFrame, rowIndex, rgStartRowIndex,
621 aDamageArea);
622 #ifdef DEBUG_TABLE_CELLMAP
623 Dump("after RemoveCell");
624 #endif
625 return;
626 }
627 rgStartRowIndex += rowCount;
628 rowIndex -= rowCount;
629 cellMap = cellMap->GetNextSibling();
630 }
631 // if we reach this point - the cell did not get removed, the caller of this routine
632 // will delete the cell and the cellmap will probably hold a reference to
633 // the deleted cell which will cause a subsequent crash when this cell is
634 // referenced later
635 NS_ERROR("nsTableCellMap::RemoveCell - could not remove cell");
636 }
638 void
639 nsTableCellMap::RebuildConsideringCells(nsCellMap* aCellMap,
640 nsTArray<nsTableCellFrame*>* aCellFrames,
641 int32_t aRowIndex,
642 int32_t aColIndex,
643 bool aInsert,
644 nsIntRect& aDamageArea)
645 {
646 int32_t numOrigCols = GetColCount();
647 ClearCols();
648 nsCellMap* cellMap = mFirstMap;
649 int32_t rowCount = 0;
650 while (cellMap) {
651 if (cellMap == aCellMap) {
652 cellMap->RebuildConsideringCells(*this, numOrigCols, aCellFrames,
653 aRowIndex, aColIndex, aInsert);
654 }
655 else {
656 cellMap->RebuildConsideringCells(*this, numOrigCols, nullptr, -1, 0,
657 false);
658 }
659 rowCount += cellMap->GetRowCount();
660 cellMap = cellMap->GetNextSibling();
661 }
662 SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea);
663 }
665 void
666 nsTableCellMap::RebuildConsideringRows(nsCellMap* aCellMap,
667 int32_t aStartRowIndex,
668 nsTArray<nsTableRowFrame*>* aRowsToInsert,
669 int32_t aNumRowsToRemove,
670 nsIntRect& aDamageArea)
671 {
672 NS_PRECONDITION(!aRowsToInsert || aNumRowsToRemove == 0,
673 "Can't handle both removing and inserting rows at once");
675 int32_t numOrigCols = GetColCount();
676 ClearCols();
677 nsCellMap* cellMap = mFirstMap;
678 int32_t rowCount = 0;
679 while (cellMap) {
680 if (cellMap == aCellMap) {
681 cellMap->RebuildConsideringRows(*this, aStartRowIndex, aRowsToInsert,
682 aNumRowsToRemove);
683 }
684 else {
685 cellMap->RebuildConsideringCells(*this, numOrigCols, nullptr, -1, 0,
686 false);
687 }
688 rowCount += cellMap->GetRowCount();
689 cellMap = cellMap->GetNextSibling();
690 }
691 SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea);
692 }
694 int32_t
695 nsTableCellMap::GetNumCellsOriginatingInCol(int32_t aColIndex) const
696 {
697 int32_t colCount = mCols.Length();
698 if ((aColIndex >= 0) && (aColIndex < colCount)) {
699 return mCols.ElementAt(aColIndex).mNumCellsOrig;
700 }
701 else {
702 NS_ERROR("nsCellMap::GetNumCellsOriginatingInCol - bad col index");
703 return 0;
704 }
705 }
707 #ifdef DEBUG
708 void
709 nsTableCellMap::Dump(char* aString) const
710 {
711 if (aString)
712 printf("%s \n", aString);
713 printf("***** START TABLE CELL MAP DUMP ***** %p\n", (void*)this);
714 // output col info
715 int32_t colCount = mCols.Length();
716 printf ("cols array orig/span-> %p", (void*)this);
717 for (int32_t colX = 0; colX < colCount; colX++) {
718 const nsColInfo& colInfo = mCols.ElementAt(colX);
719 printf ("%d=%d/%d ", colX, colInfo.mNumCellsOrig, colInfo.mNumCellsSpan);
720 }
721 printf(" cols in cache %d\n", mTableFrame.GetColCache().Length());
722 nsCellMap* cellMap = mFirstMap;
723 while (cellMap) {
724 cellMap->Dump(nullptr != mBCInfo);
725 cellMap = cellMap->GetNextSibling();
726 }
727 if (nullptr != mBCInfo) {
728 printf("***** bottom borders *****\n");
729 nscoord size;
730 BCBorderOwner owner;
731 mozilla::css::Side side;
732 bool segStart;
733 bool bevel;
734 int32_t colIndex;
735 int32_t numCols = mBCInfo->mBottomBorders.Length();
736 for (int32_t i = 0; i <= 2; i++) {
738 printf("\n ");
739 for (colIndex = 0; colIndex < numCols; colIndex++) {
740 BCData& cd = mBCInfo->mBottomBorders.ElementAt(colIndex);
741 if (0 == i) {
742 size = cd.GetTopEdge(owner, segStart);
743 printf("t=%d%X%d ", int32_t(size), owner, segStart);
744 }
745 else if (1 == i) {
746 size = cd.GetLeftEdge(owner, segStart);
747 printf("l=%d%X%d ", int32_t(size), owner, segStart);
748 }
749 else {
750 size = cd.GetCorner(side, bevel);
751 printf("c=%d%X%d ", int32_t(size), side, bevel);
752 }
753 }
754 BCData& cd = mBCInfo->mLowerRightCorner;
755 if (0 == i) {
756 size = cd.GetTopEdge(owner, segStart);
757 printf("t=%d%X%d ", int32_t(size), owner, segStart);
758 }
759 else if (1 == i) {
760 size = cd.GetLeftEdge(owner, segStart);
761 printf("l=%d%X%d ", int32_t(size), owner, segStart);
762 }
763 else {
764 size = cd.GetCorner(side, bevel);
765 printf("c=%d%X%d ", int32_t(size), side, bevel);
766 }
767 }
768 printf("\n");
769 }
770 printf("***** END TABLE CELL MAP DUMP *****\n");
771 }
772 #endif
774 nsTableCellFrame*
775 nsTableCellMap::GetCellInfoAt(int32_t aRowIndex,
776 int32_t aColIndex,
777 bool* aOriginates,
778 int32_t* aColSpan) const
779 {
780 int32_t rowIndex = aRowIndex;
781 nsCellMap* cellMap = mFirstMap;
782 while (cellMap) {
783 if (cellMap->GetRowCount() > rowIndex) {
784 return cellMap->GetCellInfoAt(*this, rowIndex, aColIndex, aOriginates, aColSpan);
785 }
786 rowIndex -= cellMap->GetRowCount();
787 cellMap = cellMap->GetNextSibling();
788 }
789 return nullptr;
790 }
792 int32_t
793 nsTableCellMap::GetIndexByRowAndColumn(int32_t aRow, int32_t aColumn) const
794 {
795 int32_t index = 0;
797 int32_t colCount = mCols.Length();
798 int32_t rowIndex = aRow;
800 nsCellMap* cellMap = mFirstMap;
801 while (cellMap) {
802 int32_t rowCount = cellMap->GetRowCount();
803 if (rowIndex >= rowCount) {
804 // If the rowCount is less than the rowIndex, this means that the index is
805 // not within the current map. If so, get the index of the last cell in
806 // the last row.
807 rowIndex -= rowCount;
809 int32_t cellMapIdx = cellMap->GetHighestIndex(colCount);
810 if (cellMapIdx != -1)
811 index += cellMapIdx + 1;
813 } else {
814 // Index is in valid range for this cellmap, so get the index of rowIndex
815 // and aColumn.
816 int32_t cellMapIdx = cellMap->GetIndexByRowAndColumn(colCount, rowIndex,
817 aColumn);
818 if (cellMapIdx == -1)
819 return -1; // no cell at the given row and column.
821 index += cellMapIdx;
822 return index; // no need to look through further maps here
823 }
825 cellMap = cellMap->GetNextSibling();
826 }
828 return -1;
829 }
831 void
832 nsTableCellMap::GetRowAndColumnByIndex(int32_t aIndex,
833 int32_t *aRow, int32_t *aColumn) const
834 {
835 *aRow = -1;
836 *aColumn = -1;
838 int32_t colCount = mCols.Length();
840 int32_t previousRows = 0;
841 int32_t index = aIndex;
843 nsCellMap* cellMap = mFirstMap;
844 while (cellMap) {
845 int32_t rowCount = cellMap->GetRowCount();
846 // Determine the highest possible index in this map to see
847 // if wanted index is in here.
848 int32_t cellMapIdx = cellMap->GetHighestIndex(colCount);
849 if (cellMapIdx == -1) {
850 // The index is not within this map, increase the total row index
851 // accordingly.
852 previousRows += rowCount;
853 } else {
854 if (index > cellMapIdx) {
855 // The index is not within this map, so decrease it by the cellMapIdx
856 // determined index and increase the total row index accordingly.
857 index -= cellMapIdx + 1;
858 previousRows += rowCount;
859 } else {
860 cellMap->GetRowAndColumnByIndex(colCount, index, aRow, aColumn);
861 // If there were previous indexes, take them into account.
862 *aRow += previousRows;
863 return; // no need to look any further.
864 }
865 }
867 cellMap = cellMap->GetNextSibling();
868 }
869 }
871 bool nsTableCellMap::RowIsSpannedInto(int32_t aRowIndex,
872 int32_t aNumEffCols) const
873 {
874 int32_t rowIndex = aRowIndex;
875 nsCellMap* cellMap = mFirstMap;
876 while (cellMap) {
877 if (cellMap->GetRowCount() > rowIndex) {
878 return cellMap->RowIsSpannedInto(rowIndex, aNumEffCols);
879 }
880 rowIndex -= cellMap->GetRowCount();
881 cellMap = cellMap->GetNextSibling();
882 }
883 return false;
884 }
886 bool nsTableCellMap::RowHasSpanningCells(int32_t aRowIndex,
887 int32_t aNumEffCols) const
888 {
889 int32_t rowIndex = aRowIndex;
890 nsCellMap* cellMap = mFirstMap;
891 while (cellMap) {
892 if (cellMap->GetRowCount() > rowIndex) {
893 return cellMap->RowHasSpanningCells(rowIndex, aNumEffCols);
894 }
895 rowIndex -= cellMap->GetRowCount();
896 cellMap = cellMap->GetNextSibling();
897 }
898 return false;
899 }
901 void nsTableCellMap::ExpandZeroColSpans()
902 {
903 mTableFrame.SetNeedColSpanExpansion(false); // mark the work done
904 mTableFrame.SetHasZeroColSpans(false); // reset the bit, if there is a
905 // zerospan it will be set again.
906 nsCellMap* cellMap = mFirstMap;
907 while (cellMap) {
908 cellMap->ExpandZeroColSpans(*this);
909 cellMap = cellMap->GetNextSibling();
910 }
911 }
913 void
914 nsTableCellMap::ResetTopStart(uint8_t aSide,
915 nsCellMap& aCellMap,
916 uint32_t aRowIndex,
917 uint32_t aColIndex,
918 bool aIsLowerRight)
919 {
920 if (!mBCInfo || aIsLowerRight) ABORT0();
922 BCCellData* cellData;
923 BCData* bcData = nullptr;
925 switch(aSide) {
926 case NS_SIDE_BOTTOM:
927 aRowIndex++;
928 // FALLTHROUGH
929 case NS_SIDE_TOP:
930 cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex);
931 if (cellData) {
932 bcData = &cellData->mData;
933 }
934 else {
935 NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error");
936 // try the next row group
937 nsCellMap* cellMap = aCellMap.GetNextSibling();
938 if (cellMap) {
939 cellData = (BCCellData*)cellMap->GetDataAt(0, aColIndex);
940 if (cellData) {
941 bcData = &cellData->mData;
942 }
943 else {
944 bcData = GetBottomMostBorder(aColIndex);
945 }
946 }
947 }
948 break;
949 case NS_SIDE_RIGHT:
950 aColIndex++;
951 // FALLTHROUGH
952 case NS_SIDE_LEFT:
953 cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex);
954 if (cellData) {
955 bcData = &cellData->mData;
956 }
957 else {
958 NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error");
959 bcData = GetRightMostBorder(aRowIndex);
960 }
961 break;
962 }
963 if (bcData) {
964 bcData->SetTopStart(false);
965 }
966 }
968 // store the aSide border segment at coord = (aRowIndex, aColIndex). For top/left, store
969 // the info at coord. For bottom/left store it at the adjacent location so that it is
970 // top/left at that location. If the new location is at the right or bottom edge of the
971 // table, then store it one of the special arrays (right most borders, bottom most borders).
972 void
973 nsTableCellMap::SetBCBorderEdge(mozilla::css::Side aSide,
974 nsCellMap& aCellMap,
975 uint32_t aCellMapStart,
976 uint32_t aRowIndex,
977 uint32_t aColIndex,
978 uint32_t aLength,
979 BCBorderOwner aOwner,
980 nscoord aSize,
981 bool aChanged)
982 {
983 if (!mBCInfo) ABORT0();
985 BCCellData* cellData;
986 int32_t lastIndex, xIndex, yIndex;
987 int32_t xPos = aColIndex;
988 int32_t yPos = aRowIndex;
989 int32_t rgYPos = aRowIndex - aCellMapStart;
990 bool changed;
992 switch(aSide) {
993 case NS_SIDE_BOTTOM:
994 rgYPos++;
995 yPos++;
996 case NS_SIDE_TOP:
997 lastIndex = xPos + aLength - 1;
998 for (xIndex = xPos; xIndex <= lastIndex; xIndex++) {
999 changed = aChanged && (xIndex == xPos);
1000 BCData* bcData = nullptr;
1001 cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xIndex);
1002 if (!cellData) {
1003 int32_t numRgRows = aCellMap.GetRowCount();
1004 if (yPos < numRgRows) { // add a dead cell data
1005 nsIntRect damageArea;
1006 cellData = (BCCellData*)aCellMap.AppendCell(*this, nullptr, rgYPos,
1007 false, 0, damageArea);
1008 if (!cellData) ABORT0();
1009 }
1010 else {
1011 NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error");
1012 // try the next non empty row group
1013 nsCellMap* cellMap = aCellMap.GetNextSibling();
1014 while (cellMap && (0 == cellMap->GetRowCount())) {
1015 cellMap = cellMap->GetNextSibling();
1016 }
1017 if (cellMap) {
1018 cellData = (BCCellData*)cellMap->GetDataAt(0, xIndex);
1019 if (!cellData) { // add a dead cell
1020 nsIntRect damageArea;
1021 cellData = (BCCellData*)cellMap->AppendCell(*this, nullptr, 0,
1022 false, 0,
1023 damageArea);
1024 }
1025 }
1026 else { // must be at the end of the table
1027 bcData = GetBottomMostBorder(xIndex);
1028 }
1029 }
1030 }
1031 if (!bcData && cellData) {
1032 bcData = &cellData->mData;
1033 }
1034 if (bcData) {
1035 bcData->SetTopEdge(aOwner, aSize, changed);
1036 }
1037 else NS_ERROR("Cellmap: Top edge not found");
1038 }
1039 break;
1040 case NS_SIDE_RIGHT:
1041 xPos++;
1042 case NS_SIDE_LEFT:
1043 // since top, bottom borders were set, there should already be a cellData entry
1044 lastIndex = rgYPos + aLength - 1;
1045 for (yIndex = rgYPos; yIndex <= lastIndex; yIndex++) {
1046 changed = aChanged && (yIndex == rgYPos);
1047 cellData = (BCCellData*)aCellMap.GetDataAt(yIndex, xPos);
1048 if (cellData) {
1049 cellData->mData.SetLeftEdge(aOwner, aSize, changed);
1050 }
1051 else {
1052 NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error");
1053 BCData* bcData = GetRightMostBorder(yIndex + aCellMapStart);
1054 if (bcData) {
1055 bcData->SetLeftEdge(aOwner, aSize, changed);
1056 }
1057 else NS_ERROR("Cellmap: Left edge not found");
1058 }
1059 }
1060 break;
1061 }
1062 }
1064 // store corner info (aOwner, aSubSize, aBevel). For aCorner = eTopLeft, store the info at
1065 // (aRowIndex, aColIndex). For eTopRight, store it in the entry to the right where
1066 // it would be top left. For eBottomRight, store it in the entry to the bottom. etc.
1067 void
1068 nsTableCellMap::SetBCBorderCorner(Corner aCorner,
1069 nsCellMap& aCellMap,
1070 uint32_t aCellMapStart,
1071 uint32_t aRowIndex,
1072 uint32_t aColIndex,
1073 mozilla::css::Side aOwner,
1074 nscoord aSubSize,
1075 bool aBevel,
1076 bool aIsBottomRight)
1077 {
1078 if (!mBCInfo) ABORT0();
1080 if (aIsBottomRight) {
1081 mBCInfo->mLowerRightCorner.SetCorner(aSubSize, aOwner, aBevel);
1082 return;
1083 }
1085 int32_t xPos = aColIndex;
1086 int32_t yPos = aRowIndex;
1087 int32_t rgYPos = aRowIndex - aCellMapStart;
1089 if (eTopRight == aCorner) {
1090 xPos++;
1091 }
1092 else if (eBottomRight == aCorner) {
1093 xPos++;
1094 rgYPos++;
1095 yPos++;
1096 }
1097 else if (eBottomLeft == aCorner) {
1098 rgYPos++;
1099 yPos++;
1100 }
1102 BCCellData* cellData = nullptr;
1103 BCData* bcData = nullptr;
1104 if (GetColCount() <= xPos) {
1105 NS_ASSERTION(xPos == GetColCount(), "program error");
1106 // at the right edge of the table as we checked the corner before
1107 NS_ASSERTION(!aIsBottomRight, "should be handled before");
1108 bcData = GetRightMostBorder(yPos);
1109 }
1110 else {
1111 cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xPos);
1112 if (!cellData) {
1113 int32_t numRgRows = aCellMap.GetRowCount();
1114 if (yPos < numRgRows) { // add a dead cell data
1115 nsIntRect damageArea;
1116 cellData = (BCCellData*)aCellMap.AppendCell(*this, nullptr, rgYPos,
1117 false, 0, damageArea);
1118 }
1119 else {
1120 // try the next non empty row group
1121 nsCellMap* cellMap = aCellMap.GetNextSibling();
1122 while (cellMap && (0 == cellMap->GetRowCount())) {
1123 cellMap = cellMap->GetNextSibling();
1124 }
1125 if (cellMap) {
1126 cellData = (BCCellData*)cellMap->GetDataAt(0, xPos);
1127 if (!cellData) { // add a dead cell
1128 nsIntRect damageArea;
1129 cellData = (BCCellData*)cellMap->AppendCell(*this, nullptr, 0,
1130 false, 0, damageArea);
1131 }
1132 }
1133 else { // must be at the bottom of the table
1134 bcData = GetBottomMostBorder(xPos);
1135 }
1136 }
1137 }
1138 }
1139 if (!bcData && cellData) {
1140 bcData = &cellData->mData;
1141 }
1142 if (bcData) {
1143 bcData->SetCorner(aSubSize, aOwner, aBevel);
1144 }
1145 else NS_ERROR("program error: Corner not found");
1146 }
1148 nsCellMap::nsCellMap(nsTableRowGroupFrame* aRowGroup, bool aIsBC)
1149 : mRows(8), mContentRowCount(0), mRowGroupFrame(aRowGroup),
1150 mNextSibling(nullptr), mIsBC(aIsBC),
1151 mPresContext(aRowGroup->PresContext())
1152 {
1153 MOZ_COUNT_CTOR(nsCellMap);
1154 NS_ASSERTION(mPresContext, "Must have prescontext");
1155 }
1157 nsCellMap::~nsCellMap()
1158 {
1159 MOZ_COUNT_DTOR(nsCellMap);
1161 uint32_t mapRowCount = mRows.Length();
1162 for (uint32_t rowX = 0; rowX < mapRowCount; rowX++) {
1163 CellDataArray &row = mRows[rowX];
1164 uint32_t colCount = row.Length();
1165 for (uint32_t colX = 0; colX < colCount; colX++) {
1166 DestroyCellData(row[colX]);
1167 }
1168 }
1169 }
1171 /* static */
1172 void
1173 nsCellMap::Init()
1174 {
1175 NS_ABORT_IF_FALSE(!sEmptyRow, "How did that happen?");
1176 sEmptyRow = new nsCellMap::CellDataArray();
1177 }
1179 /* static */
1180 void
1181 nsCellMap::Shutdown()
1182 {
1183 delete sEmptyRow;
1184 sEmptyRow = nullptr;
1185 }
1187 nsTableCellFrame*
1188 nsCellMap::GetCellFrame(int32_t aRowIndexIn,
1189 int32_t aColIndexIn,
1190 CellData& aData,
1191 bool aUseRowIfOverlap) const
1192 {
1193 int32_t rowIndex = aRowIndexIn - aData.GetRowSpanOffset();
1194 int32_t colIndex = aColIndexIn - aData.GetColSpanOffset();
1195 if (aData.IsOverlap()) {
1196 if (aUseRowIfOverlap) {
1197 colIndex = aColIndexIn;
1198 }
1199 else {
1200 rowIndex = aRowIndexIn;
1201 }
1202 }
1204 CellData* data =
1205 mRows.SafeElementAt(rowIndex, *sEmptyRow).SafeElementAt(colIndex);
1206 if (data) {
1207 return data->GetCellFrame();
1208 }
1209 return nullptr;
1210 }
1212 int32_t
1213 nsCellMap::GetHighestIndex(int32_t aColCount)
1214 {
1215 int32_t index = -1;
1216 int32_t rowCount = mRows.Length();
1217 for (int32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
1218 const CellDataArray& row = mRows[rowIdx];
1220 for (int32_t colIdx = 0; colIdx < aColCount; colIdx++) {
1221 CellData* data = row.SafeElementAt(colIdx);
1222 // No data means row doesn't have more cells.
1223 if (!data)
1224 break;
1226 if (data->IsOrig())
1227 index++;
1228 }
1229 }
1231 return index;
1232 }
1234 int32_t
1235 nsCellMap::GetIndexByRowAndColumn(int32_t aColCount,
1236 int32_t aRow, int32_t aColumn) const
1237 {
1238 if (uint32_t(aRow) >= mRows.Length())
1239 return -1;
1241 int32_t index = -1;
1242 int32_t lastColsIdx = aColCount - 1;
1244 // Find row index of the cell where row span is started.
1245 const CellDataArray& row = mRows[aRow];
1246 CellData* data = row.SafeElementAt(aColumn);
1247 int32_t origRow = data ? aRow - data->GetRowSpanOffset() : aRow;
1249 // Calculate cell index.
1250 for (int32_t rowIdx = 0; rowIdx <= origRow; rowIdx++) {
1251 const CellDataArray& row = mRows[rowIdx];
1252 int32_t colCount = (rowIdx == origRow) ? aColumn : lastColsIdx;
1254 for (int32_t colIdx = 0; colIdx <= colCount; colIdx++) {
1255 data = row.SafeElementAt(colIdx);
1256 // No data means row doesn't have more cells.
1257 if (!data)
1258 break;
1260 if (data->IsOrig())
1261 index++;
1262 }
1263 }
1265 // Given row and column don't point to the cell.
1266 if (!data)
1267 return -1;
1269 return index;
1270 }
1272 void
1273 nsCellMap::GetRowAndColumnByIndex(int32_t aColCount, int32_t aIndex,
1274 int32_t *aRow, int32_t *aColumn) const
1275 {
1276 *aRow = -1;
1277 *aColumn = -1;
1279 int32_t index = aIndex;
1280 int32_t rowCount = mRows.Length();
1282 for (int32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) {
1283 const CellDataArray& row = mRows[rowIdx];
1285 for (int32_t colIdx = 0; colIdx < aColCount; colIdx++) {
1286 CellData* data = row.SafeElementAt(colIdx);
1288 // The row doesn't have more cells.
1289 if (!data)
1290 break;
1292 if (data->IsOrig())
1293 index--;
1295 if (index < 0) {
1296 *aRow = rowIdx;
1297 *aColumn = colIdx;
1298 return;
1299 }
1300 }
1301 }
1302 }
1304 bool nsCellMap::Grow(nsTableCellMap& aMap,
1305 int32_t aNumRows,
1306 int32_t aRowIndex)
1307 {
1308 NS_ASSERTION(aNumRows >= 1, "Why are we calling this?");
1310 // Get the number of cols we want to use for preallocating the row arrays.
1311 int32_t numCols = aMap.GetColCount();
1312 if (numCols == 0) {
1313 numCols = 4;
1314 }
1315 uint32_t startRowIndex = (aRowIndex >= 0) ? aRowIndex : mRows.Length();
1316 NS_ASSERTION(startRowIndex <= mRows.Length(), "Missing grow call inbetween");
1318 return mRows.InsertElementsAt(startRowIndex, aNumRows, numCols) != nullptr;
1319 }
1321 void nsCellMap::GrowRow(CellDataArray& aRow,
1322 int32_t aNumCols)
1324 {
1325 // Have to have the cast to get the template to do the right thing.
1326 aRow.InsertElementsAt(aRow.Length(), aNumCols, (CellData*)nullptr);
1327 }
1329 void
1330 nsCellMap::InsertRows(nsTableCellMap& aMap,
1331 nsTArray<nsTableRowFrame*>& aRows,
1332 int32_t aFirstRowIndex,
1333 bool aConsiderSpans,
1334 int32_t aRgFirstRowIndex,
1335 nsIntRect& aDamageArea)
1336 {
1337 int32_t numCols = aMap.GetColCount();
1338 NS_ASSERTION(aFirstRowIndex >= 0, "nsCellMap::InsertRows called with negative rowIndex");
1339 if (uint32_t(aFirstRowIndex) > mRows.Length()) {
1340 // create (aFirstRowIndex - mRows.Length()) empty rows up to aFirstRowIndex
1341 int32_t numEmptyRows = aFirstRowIndex - mRows.Length();
1342 if (!Grow(aMap, numEmptyRows)) {
1343 return;
1344 }
1345 }
1347 if (!aConsiderSpans) {
1348 // update mContentRowCount, since non-empty rows will be added
1349 mContentRowCount = std::max(aFirstRowIndex, mContentRowCount);
1350 ExpandWithRows(aMap, aRows, aFirstRowIndex, aRgFirstRowIndex, aDamageArea);
1351 return;
1352 }
1354 // if any cells span into or out of the row being inserted, then rebuild
1355 bool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex,
1356 aFirstRowIndex, 0, numCols - 1);
1358 // update mContentRowCount, since non-empty rows will be added
1359 mContentRowCount = std::max(aFirstRowIndex, mContentRowCount);
1361 // if any of the new cells span out of the new rows being added, then rebuild
1362 // XXX it would be better to only rebuild the portion of the map that follows the new rows
1363 if (!spansCauseRebuild && (uint32_t(aFirstRowIndex) < mRows.Length())) {
1364 spansCauseRebuild = CellsSpanOut(aRows);
1365 }
1366 if (spansCauseRebuild) {
1367 aMap.RebuildConsideringRows(this, aFirstRowIndex, &aRows, 0, aDamageArea);
1368 }
1369 else {
1370 ExpandWithRows(aMap, aRows, aFirstRowIndex, aRgFirstRowIndex, aDamageArea);
1371 }
1372 }
1374 void
1375 nsCellMap::RemoveRows(nsTableCellMap& aMap,
1376 int32_t aFirstRowIndex,
1377 int32_t aNumRowsToRemove,
1378 bool aConsiderSpans,
1379 int32_t aRgFirstRowIndex,
1380 nsIntRect& aDamageArea)
1381 {
1382 int32_t numRows = mRows.Length();
1383 int32_t numCols = aMap.GetColCount();
1385 if (aFirstRowIndex >= numRows) {
1386 // reduce the content based row count based on the function arguments
1387 // as they are known to be real rows even if the cell map did not create
1388 // rows for them before.
1389 mContentRowCount -= aNumRowsToRemove;
1390 return;
1391 }
1392 if (!aConsiderSpans) {
1393 ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aRgFirstRowIndex,
1394 aDamageArea);
1395 return;
1396 }
1397 int32_t endRowIndex = aFirstRowIndex + aNumRowsToRemove - 1;
1398 if (endRowIndex >= numRows) {
1399 NS_ERROR("nsCellMap::RemoveRows tried to remove too many rows");
1400 endRowIndex = numRows - 1;
1401 }
1402 bool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex, endRowIndex,
1403 0, numCols - 1);
1404 if (spansCauseRebuild) {
1405 aMap.RebuildConsideringRows(this, aFirstRowIndex, nullptr, aNumRowsToRemove,
1406 aDamageArea);
1407 }
1408 else {
1409 ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aRgFirstRowIndex,
1410 aDamageArea);
1411 }
1412 }
1417 CellData*
1418 nsCellMap::AppendCell(nsTableCellMap& aMap,
1419 nsTableCellFrame* aCellFrame,
1420 int32_t aRowIndex,
1421 bool aRebuildIfNecessary,
1422 int32_t aRgFirstRowIndex,
1423 nsIntRect& aDamageArea,
1424 int32_t* aColToBeginSearch)
1425 {
1426 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1427 int32_t origNumMapRows = mRows.Length();
1428 int32_t origNumCols = aMap.GetColCount();
1429 bool zeroRowSpan = false;
1430 int32_t rowSpan = (aCellFrame) ? GetRowSpanForNewCell(aCellFrame, aRowIndex,
1431 zeroRowSpan) : 1;
1432 // add new rows if necessary
1433 int32_t endRowIndex = aRowIndex + rowSpan - 1;
1434 if (endRowIndex >= origNumMapRows) {
1435 // XXXbz handle allocation failures?
1436 Grow(aMap, 1 + endRowIndex - origNumMapRows);
1437 }
1439 // get the first null or dead CellData in the desired row. It will equal origNumCols if there are none
1440 CellData* origData = nullptr;
1441 int32_t startColIndex = 0;
1442 if (aColToBeginSearch)
1443 startColIndex = *aColToBeginSearch;
1444 for (; startColIndex < origNumCols; startColIndex++) {
1445 CellData* data = GetDataAt(aRowIndex, startColIndex);
1446 if (!data)
1447 break;
1448 // The border collapse code relies on having multiple dead cell data entries
1449 // in a row.
1450 if (data->IsDead() && aCellFrame) {
1451 origData = data;
1452 break;
1453 }
1454 if (data->IsZeroColSpan() ) {
1455 // appending a cell collapses zerospans.
1456 CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex);
1457 // ask again for the data as it should be modified
1458 origData = GetDataAt(aRowIndex, startColIndex);
1459 NS_ASSERTION(origData->IsDead(),
1460 "The cellposition should have been cleared");
1461 break;
1462 }
1463 }
1464 // We found the place to append the cell, when the next cell is appended
1465 // the next search does not need to duplicate the search but can start
1466 // just at the next cell.
1467 if (aColToBeginSearch)
1468 *aColToBeginSearch = startColIndex + 1;
1470 bool zeroColSpan = false;
1471 int32_t colSpan = (aCellFrame) ?
1472 GetColSpanForNewCell(*aCellFrame, zeroColSpan) : 1;
1473 if (zeroColSpan) {
1474 aMap.mTableFrame.SetHasZeroColSpans(true);
1475 aMap.mTableFrame.SetNeedColSpanExpansion(true);
1476 }
1478 // if the new cell could potentially span into other rows and collide with
1479 // originating cells there, we will play it safe and just rebuild the map
1480 if (aRebuildIfNecessary && (aRowIndex < mContentRowCount - 1) && (rowSpan > 1)) {
1481 nsAutoTArray<nsTableCellFrame*, 1> newCellArray;
1482 newCellArray.AppendElement(aCellFrame);
1483 aMap.RebuildConsideringCells(this, &newCellArray, aRowIndex, startColIndex, true, aDamageArea);
1484 return origData;
1485 }
1486 mContentRowCount = std::max(mContentRowCount, aRowIndex + 1);
1488 // add new cols to the table map if necessary
1489 int32_t endColIndex = startColIndex + colSpan - 1;
1490 if (endColIndex >= origNumCols) {
1491 NS_ASSERTION(aCellFrame, "dead cells should not require new columns");
1492 aMap.AddColsAtEnd(1 + endColIndex - origNumCols);
1493 }
1495 // Setup CellData for this cell
1496 if (origData) {
1497 NS_ASSERTION(origData->IsDead(), "replacing a non dead cell is a memory leak");
1498 if (aCellFrame) { // do nothing to replace a dead cell with a dead cell
1499 origData->Init(aCellFrame);
1500 // we are replacing a dead cell, increase the number of cells
1501 // originating at this column
1502 nsColInfo* colInfo = aMap.GetColInfoAt(startColIndex);
1503 NS_ASSERTION(colInfo, "access to a non existing column");
1504 if (colInfo) {
1505 colInfo->mNumCellsOrig++;
1506 }
1507 }
1508 }
1509 else {
1510 origData = AllocCellData(aCellFrame);
1511 if (!origData) ABORT1(origData);
1512 SetDataAt(aMap, *origData, aRowIndex, startColIndex);
1513 }
1515 if (aRebuildIfNecessary) {
1516 //the caller depends on the damageArea
1517 // The special case for zeroRowSpan is to adjust for the '2' in
1518 // GetRowSpanForNewCell.
1519 uint32_t height = zeroRowSpan ? endRowIndex - aRowIndex :
1520 1 + endRowIndex - aRowIndex;
1521 SetDamageArea(startColIndex, aRgFirstRowIndex + aRowIndex,
1522 1 + endColIndex - startColIndex, height, aDamageArea);
1523 }
1525 if (!aCellFrame) {
1526 return origData;
1527 }
1529 // initialize the cell frame
1530 aCellFrame->SetColIndex(startColIndex);
1532 // Create CellData objects for the rows that this cell spans. Set
1533 // their mOrigCell to nullptr and their mSpanData to point to data.
1534 for (int32_t rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1535 // The row at rowX will need to have at least endColIndex columns
1536 mRows[rowX].SetCapacity(endColIndex);
1537 for (int32_t colX = startColIndex; colX <= endColIndex; colX++) {
1538 if ((rowX != aRowIndex) || (colX != startColIndex)) { // skip orig cell data done above
1539 CellData* cellData = GetDataAt(rowX, colX);
1540 if (cellData) {
1541 if (cellData->IsOrig()) {
1542 NS_ERROR("cannot overlap originating cell");
1543 continue;
1544 }
1545 if (rowX > aRowIndex) { // row spanning into cell
1546 if (cellData->IsRowSpan()) {
1547 // do nothing, this can be caused by rowspan which is overlapped
1548 // by a another cell with a rowspan and a colspan
1549 }
1550 else {
1551 cellData->SetRowSpanOffset(rowX - aRowIndex);
1552 if (zeroRowSpan) {
1553 cellData->SetZeroRowSpan(true);
1554 }
1555 }
1556 }
1557 if (colX > startColIndex) { // col spanning into cell
1558 if (!cellData->IsColSpan()) {
1559 if (cellData->IsRowSpan()) {
1560 cellData->SetOverlap(true);
1561 }
1562 cellData->SetColSpanOffset(colX - startColIndex);
1563 if (zeroColSpan) {
1564 cellData->SetZeroColSpan(true);
1565 }
1567 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1568 colInfo->mNumCellsSpan++;
1569 }
1570 }
1571 }
1572 else {
1573 cellData = AllocCellData(nullptr);
1574 if (!cellData) return origData;
1575 if (rowX > aRowIndex) {
1576 cellData->SetRowSpanOffset(rowX - aRowIndex);
1577 if (zeroRowSpan) {
1578 cellData->SetZeroRowSpan(true);
1579 }
1580 }
1581 if (colX > startColIndex) {
1582 cellData->SetColSpanOffset(colX - startColIndex);
1583 if (zeroColSpan) {
1584 cellData->SetZeroColSpan(true);
1585 }
1586 }
1587 SetDataAt(aMap, *cellData, rowX, colX);
1588 }
1589 }
1590 }
1591 }
1592 #ifdef DEBUG_TABLE_CELLMAP
1593 printf("appended cell=%p row=%d \n", aCellFrame, aRowIndex);
1594 aMap.Dump();
1595 #endif
1596 return origData;
1597 }
1599 void nsCellMap::CollapseZeroColSpan(nsTableCellMap& aMap,
1600 CellData* aOrigData,
1601 int32_t aRowIndex,
1602 int32_t aColIndex)
1603 {
1604 // if after a colspan = 0 cell another cell is appended in a row the html 4
1605 // spec is already violated. In principle one should then append the cell
1606 // after the last column but then the zero spanning cell would also have
1607 // to grow. The only plausible way to break this cycle is ignore the zero
1608 // colspan and reset the cell to colspan = 1.
1610 NS_ASSERTION(aOrigData && aOrigData->IsZeroColSpan(),
1611 "zero colspan should have been passed");
1612 // find the originating cellframe
1613 nsTableCellFrame* cell = GetCellFrame(aRowIndex, aColIndex, *aOrigData, true);
1614 NS_ASSERTION(cell, "originating cell not found");
1616 // find the clearing region
1617 int32_t startRowIndex = aRowIndex - aOrigData->GetRowSpanOffset();
1618 bool zeroSpan;
1619 int32_t rowSpan = GetRowSpanForNewCell(cell, startRowIndex, zeroSpan);
1620 int32_t endRowIndex = startRowIndex + rowSpan;
1622 int32_t origColIndex = aColIndex - aOrigData->GetColSpanOffset();
1623 int32_t endColIndex = origColIndex +
1624 GetEffectiveColSpan(aMap, startRowIndex,
1625 origColIndex, zeroSpan);
1626 for (int32_t colX = origColIndex +1; colX < endColIndex; colX++) {
1627 // Start the collapse just after the originating cell, since
1628 // we're basically making the originating cell act as if it
1629 // has colspan="1".
1630 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1631 colInfo->mNumCellsSpan -= rowSpan;
1633 for (int32_t rowX = startRowIndex; rowX < endRowIndex; rowX++)
1634 {
1635 CellData* data = mRows[rowX][colX];
1636 NS_ASSERTION(data->IsZeroColSpan(),
1637 "Overwriting previous data - memory leak");
1638 data->Init(nullptr); // mark the cell as a dead cell.
1639 }
1640 }
1641 }
1643 bool nsCellMap::CellsSpanOut(nsTArray<nsTableRowFrame*>& aRows) const
1644 {
1645 int32_t numNewRows = aRows.Length();
1646 for (int32_t rowX = 0; rowX < numNewRows; rowX++) {
1647 nsIFrame* rowFrame = (nsIFrame *) aRows.ElementAt(rowX);
1648 nsIFrame* childFrame = rowFrame->GetFirstPrincipalChild();
1649 while (childFrame) {
1650 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
1651 if (cellFrame) {
1652 bool zeroSpan;
1653 int32_t rowSpan = GetRowSpanForNewCell(cellFrame, rowX, zeroSpan);
1654 if (zeroSpan || rowX + rowSpan > numNewRows) {
1655 return true;
1656 }
1657 }
1658 childFrame = childFrame->GetNextSibling();
1659 }
1660 }
1661 return false;
1662 }
1664 // return true if any cells have rows spans into or out of the region
1665 // defined by the row and col indices or any cells have colspans into the region
1666 bool nsCellMap::CellsSpanInOrOut(int32_t aStartRowIndex,
1667 int32_t aEndRowIndex,
1668 int32_t aStartColIndex,
1669 int32_t aEndColIndex) const
1670 {
1671 /*
1672 * this routine will watch the cells adjacent to the region or at the edge
1673 * they are marked with *. The routine will verify whether they span in or
1674 * are spanned out.
1675 *
1676 * startCol endCol
1677 * r1c1 r1c2 r1c3 r1c4 r1c5 r1rc6 r1c7
1678 * startrow r2c1 r2c2 *r2c3 *r2c4 *r2c5 *r2rc6 r2c7
1679 * endrow r3c1 r3c2 *r3c3 r3c4 r3c5 *r3rc6 r3c7
1680 * r4c1 r4c2 *r4c3 *r4c4 *r4c5 r4rc6 r4c7
1681 * r5c1 r5c2 r5c3 r5c4 r5c5 r5rc6 r5c7
1682 */
1684 int32_t numRows = mRows.Length(); // use the cellmap rows to determine the
1685 // current cellmap extent.
1686 for (int32_t colX = aStartColIndex; colX <= aEndColIndex; colX++) {
1687 CellData* cellData;
1688 if (aStartRowIndex > 0) {
1689 cellData = GetDataAt(aStartRowIndex, colX);
1690 if (cellData && (cellData->IsRowSpan())) {
1691 return true; // there is a row span into the region
1692 }
1693 if ((aStartRowIndex >= mContentRowCount) && (mContentRowCount > 0)) {
1694 cellData = GetDataAt(mContentRowCount - 1, colX);
1695 if (cellData && cellData->IsZeroRowSpan()) {
1696 return true; // When we expand the zerospan it'll span into our row
1697 }
1698 }
1699 }
1700 if (aEndRowIndex < numRows - 1) { // is there anything below aEndRowIndex
1701 cellData = GetDataAt(aEndRowIndex + 1, colX);
1702 if ((cellData) && (cellData->IsRowSpan())) {
1703 return true; // there is a row span out of the region
1704 }
1705 }
1706 else {
1707 cellData = GetDataAt(aEndRowIndex, colX);
1708 if ((cellData) && (cellData->IsRowSpan()) && (mContentRowCount < numRows)) {
1709 return true; // this cell might be the cause of a dead row
1710 }
1711 }
1712 }
1713 if (aStartColIndex > 0) {
1714 for (int32_t rowX = aStartRowIndex; rowX <= aEndRowIndex; rowX++) {
1715 CellData* cellData = GetDataAt(rowX, aStartColIndex);
1716 if (cellData && (cellData->IsColSpan())) {
1717 return true; // there is a col span into the region
1718 }
1719 cellData = GetDataAt(rowX, aEndColIndex + 1);
1720 if (cellData && (cellData->IsColSpan())) {
1721 return true; // there is a col span out of the region
1722 }
1723 }
1724 }
1725 return false;
1726 }
1728 void nsCellMap::InsertCells(nsTableCellMap& aMap,
1729 nsTArray<nsTableCellFrame*>& aCellFrames,
1730 int32_t aRowIndex,
1731 int32_t aColIndexBefore,
1732 int32_t aRgFirstRowIndex,
1733 nsIntRect& aDamageArea)
1734 {
1735 if (aCellFrames.Length() == 0) return;
1736 NS_ASSERTION(aColIndexBefore >= -1, "index out of range");
1737 int32_t numCols = aMap.GetColCount();
1738 if (aColIndexBefore >= numCols) {
1739 NS_ERROR("Inserting instead of appending cells indicates a serious cellmap error");
1740 aColIndexBefore = numCols - 1;
1741 }
1743 // get the starting col index of the 1st new cells
1744 int32_t startColIndex;
1745 for (startColIndex = aColIndexBefore + 1; startColIndex < numCols; startColIndex++) {
1746 CellData* data = GetDataAt(aRowIndex, startColIndex);
1747 if (!data || data->IsOrig() || data->IsDead()) {
1748 // // Not a span. Stop.
1749 break;
1750 }
1751 if (data->IsZeroColSpan()) {
1752 // Zero colspans collapse. Stop in this case too.
1753 CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex);
1754 break;
1755 }
1756 }
1758 // record whether inserted cells are going to cause complications due
1759 // to existing row spans, col spans or table sizing.
1760 bool spansCauseRebuild = false;
1762 // check that all cells have the same row span
1763 int32_t numNewCells = aCellFrames.Length();
1764 bool zeroRowSpan = false;
1765 int32_t rowSpan = 0;
1766 for (int32_t cellX = 0; cellX < numNewCells; cellX++) {
1767 nsTableCellFrame* cell = aCellFrames.ElementAt(cellX);
1768 int32_t rowSpan2 = GetRowSpanForNewCell(cell, aRowIndex, zeroRowSpan);
1769 if (rowSpan == 0) {
1770 rowSpan = rowSpan2;
1771 }
1772 else if (rowSpan != rowSpan2) {
1773 spansCauseRebuild = true;
1774 break;
1775 }
1776 }
1778 // check if the new cells will cause the table to add more rows
1779 if (!spansCauseRebuild) {
1780 if (mRows.Length() < uint32_t(aRowIndex + rowSpan)) {
1781 spansCauseRebuild = true;
1782 }
1783 }
1785 if (!spansCauseRebuild) {
1786 spansCauseRebuild = CellsSpanInOrOut(aRowIndex, aRowIndex + rowSpan - 1,
1787 startColIndex, numCols - 1);
1788 }
1789 if (spansCauseRebuild) {
1790 aMap.RebuildConsideringCells(this, &aCellFrames, aRowIndex, startColIndex,
1791 true, aDamageArea);
1792 }
1793 else {
1794 ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan,
1795 zeroRowSpan, aRgFirstRowIndex, aDamageArea);
1796 }
1797 }
1799 void
1800 nsCellMap::ExpandWithRows(nsTableCellMap& aMap,
1801 nsTArray<nsTableRowFrame*>& aRowFrames,
1802 int32_t aStartRowIndexIn,
1803 int32_t aRgFirstRowIndex,
1804 nsIntRect& aDamageArea)
1805 {
1806 int32_t startRowIndex = (aStartRowIndexIn >= 0) ? aStartRowIndexIn : 0;
1807 NS_ASSERTION(uint32_t(startRowIndex) <= mRows.Length(), "caller should have grown cellmap before");
1809 int32_t numNewRows = aRowFrames.Length();
1810 mContentRowCount += numNewRows;
1812 int32_t endRowIndex = startRowIndex + numNewRows - 1;
1814 // shift the rows after startRowIndex down and insert empty rows that will
1815 // be filled via the AppendCell call below
1816 if (!Grow(aMap, numNewRows, startRowIndex)) {
1817 return;
1818 }
1821 int32_t newRowIndex = 0;
1822 for (int32_t rowX = startRowIndex; rowX <= endRowIndex; rowX++) {
1823 nsTableRowFrame* rFrame = aRowFrames.ElementAt(newRowIndex);
1824 // append cells
1825 nsIFrame* cFrame = rFrame->GetFirstPrincipalChild();
1826 int32_t colIndex = 0;
1827 while (cFrame) {
1828 nsTableCellFrame *cellFrame = do_QueryFrame(cFrame);
1829 if (cellFrame) {
1830 AppendCell(aMap, cellFrame, rowX, false, aRgFirstRowIndex, aDamageArea,
1831 &colIndex);
1832 }
1833 cFrame = cFrame->GetNextSibling();
1834 }
1835 newRowIndex++;
1836 }
1837 // mark all following rows damaged, they might contain a previously set
1838 // damage area which we can not shift.
1839 int32_t firstDamagedRow = aRgFirstRowIndex + startRowIndex;
1840 SetDamageArea(0, firstDamagedRow, aMap.GetColCount(),
1841 aMap.GetRowCount() - firstDamagedRow, aDamageArea);
1842 }
1844 void nsCellMap::ExpandWithCells(nsTableCellMap& aMap,
1845 nsTArray<nsTableCellFrame*>& aCellFrames,
1846 int32_t aRowIndex,
1847 int32_t aColIndex,
1848 int32_t aRowSpan, // same for all cells
1849 bool aRowSpanIsZero,
1850 int32_t aRgFirstRowIndex,
1851 nsIntRect& aDamageArea)
1852 {
1853 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1854 int32_t endRowIndex = aRowIndex + aRowSpan - 1;
1855 int32_t startColIndex = aColIndex;
1856 int32_t endColIndex = aColIndex;
1857 int32_t numCells = aCellFrames.Length();
1858 int32_t totalColSpan = 0;
1860 // add cellData entries for the space taken up by the new cells
1861 for (int32_t cellX = 0; cellX < numCells; cellX++) {
1862 nsTableCellFrame* cellFrame = aCellFrames.ElementAt(cellX);
1863 CellData* origData = AllocCellData(cellFrame); // the originating cell
1864 if (!origData) return;
1866 // set the starting and ending col index for the new cell
1867 bool zeroColSpan = false;
1868 int32_t colSpan = GetColSpanForNewCell(*cellFrame, zeroColSpan);
1869 if (zeroColSpan) {
1870 aMap.mTableFrame.SetHasZeroColSpans(true);
1871 aMap.mTableFrame.SetNeedColSpanExpansion(true);
1872 }
1873 totalColSpan += colSpan;
1874 if (cellX == 0) {
1875 endColIndex = aColIndex + colSpan - 1;
1876 }
1877 else {
1878 startColIndex = endColIndex + 1;
1879 endColIndex = startColIndex + colSpan - 1;
1880 }
1882 // add the originating cell data and any cell data corresponding to row/col spans
1883 for (int32_t rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1884 CellDataArray& row = mRows[rowX];
1885 // Pre-allocate all the cells we'll need in this array, setting
1886 // them to null.
1887 // Have to have the cast to get the template to do the right thing.
1888 int32_t insertionIndex = row.Length();
1889 if (insertionIndex > startColIndex) {
1890 insertionIndex = startColIndex;
1891 }
1892 if (!row.InsertElementsAt(insertionIndex, endColIndex - insertionIndex + 1,
1893 (CellData*)nullptr) &&
1894 rowX == aRowIndex) {
1895 // Failed to insert the slots, and this is the very first row. That
1896 // means that we need to clean up |origData| before returning, since
1897 // the cellmap doesn't own it yet.
1898 DestroyCellData(origData);
1899 return;
1900 }
1902 for (int32_t colX = startColIndex; colX <= endColIndex; colX++) {
1903 CellData* data = origData;
1904 if ((rowX != aRowIndex) || (colX != startColIndex)) {
1905 data = AllocCellData(nullptr);
1906 if (!data) return;
1907 if (rowX > aRowIndex) {
1908 data->SetRowSpanOffset(rowX - aRowIndex);
1909 if (aRowSpanIsZero) {
1910 data->SetZeroRowSpan(true);
1911 }
1912 }
1913 if (colX > startColIndex) {
1914 data->SetColSpanOffset(colX - startColIndex);
1915 if (zeroColSpan) {
1916 data->SetZeroColSpan(true);
1917 }
1918 }
1919 }
1920 SetDataAt(aMap, *data, rowX, colX);
1921 }
1922 }
1923 cellFrame->SetColIndex(startColIndex);
1924 }
1925 int32_t damageHeight = std::min(GetRowGroup()->GetRowCount() - aRowIndex,
1926 aRowSpan);
1927 SetDamageArea(aColIndex, aRgFirstRowIndex + aRowIndex,
1928 1 + endColIndex - aColIndex, damageHeight, aDamageArea);
1930 int32_t rowX;
1932 // update the row and col info due to shifting
1933 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
1934 CellDataArray& row = mRows[rowX];
1935 uint32_t numCols = row.Length();
1936 uint32_t colX;
1937 for (colX = aColIndex + totalColSpan; colX < numCols; colX++) {
1938 CellData* data = row[colX];
1939 if (data) {
1940 // increase the origin and span counts beyond the spanned cols
1941 if (data->IsOrig()) {
1942 // a cell that gets moved needs adjustment as well as it new orignating col
1943 data->GetCellFrame()->SetColIndex(colX);
1944 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1945 colInfo->mNumCellsOrig++;
1946 }
1947 if (data->IsColSpan()) {
1948 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1949 colInfo->mNumCellsSpan++;
1950 }
1952 // decrease the origin and span counts within the spanned cols
1953 int32_t colX2 = colX - totalColSpan;
1954 nsColInfo* colInfo2 = aMap.GetColInfoAt(colX2);
1955 if (data->IsOrig()) {
1956 // the old originating col of a moved cell needs adjustment
1957 colInfo2->mNumCellsOrig--;
1958 }
1959 if (data->IsColSpan()) {
1960 colInfo2->mNumCellsSpan--;
1961 }
1962 }
1963 }
1964 }
1965 }
1967 void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap,
1968 int32_t aStartRowIndex,
1969 int32_t aNumRowsToRemove,
1970 int32_t aRgFirstRowIndex,
1971 nsIntRect& aDamageArea)
1972 {
1973 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
1974 int32_t endRowIndex = aStartRowIndex + aNumRowsToRemove - 1;
1975 uint32_t colCount = aMap.GetColCount();
1976 for (int32_t rowX = endRowIndex; rowX >= aStartRowIndex; --rowX) {
1977 CellDataArray& row = mRows[rowX];
1978 uint32_t colX;
1979 for (colX = 0; colX < colCount; colX++) {
1980 CellData* data = row.SafeElementAt(colX);
1981 if (data) {
1982 // Adjust the column counts.
1983 if (data->IsOrig()) {
1984 // Decrement the column count.
1985 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1986 colInfo->mNumCellsOrig--;
1987 }
1988 // colspan=0 is only counted as a spanned cell in the 1st col it spans
1989 else if (data->IsColSpan()) {
1990 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
1991 colInfo->mNumCellsSpan--;
1992 }
1993 }
1994 }
1996 uint32_t rowLength = row.Length();
1997 // Delete our row information.
1998 for (colX = 0; colX < rowLength; colX++) {
1999 DestroyCellData(row[colX]);
2000 }
2002 mRows.RemoveElementAt(rowX);
2004 // Decrement our row and next available index counts.
2005 mContentRowCount--;
2006 }
2007 aMap.RemoveColsAtEnd();
2008 // mark all following rows damaged, they might contain a previously set
2009 // damage area which we can not shift.
2010 int32_t firstDamagedRow = aRgFirstRowIndex + aStartRowIndex;
2011 SetDamageArea(0, firstDamagedRow, aMap.GetColCount(),
2012 aMap.GetRowCount() - firstDamagedRow, aDamageArea);
2013 }
2015 int32_t nsCellMap::GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd,
2016 bool& aIsZeroColSpan) const
2017 {
2018 aIsZeroColSpan = false;
2019 int32_t colSpan = aCellFrameToAdd.GetColSpan();
2020 if (0 == colSpan) {
2021 colSpan = 1; // set the min colspan it will be expanded later
2022 aIsZeroColSpan = true;
2023 }
2024 return colSpan;
2025 }
2027 int32_t nsCellMap::GetEffectiveColSpan(const nsTableCellMap& aMap,
2028 int32_t aRowIndex,
2029 int32_t aColIndex,
2030 bool& aZeroColSpan) const
2031 {
2032 int32_t numColsInTable = aMap.GetColCount();
2033 aZeroColSpan = false;
2034 int32_t colSpan = 1;
2035 if (uint32_t(aRowIndex) >= mRows.Length()) {
2036 return colSpan;
2037 }
2039 const CellDataArray& row = mRows[aRowIndex];
2040 int32_t colX;
2041 CellData* data;
2042 int32_t maxCols = numColsInTable;
2043 bool hitOverlap = false; // XXX this is not ever being set to true
2044 for (colX = aColIndex + 1; colX < maxCols; colX++) {
2045 data = row.SafeElementAt(colX);
2046 if (data) {
2047 // for an overlapping situation get the colspan from the originating cell and
2048 // use that as the max number of cols to iterate. Since this is rare, only
2049 // pay the price of looking up the cell's colspan here.
2050 if (!hitOverlap && data->IsOverlap()) {
2051 CellData* origData = row.SafeElementAt(aColIndex);
2052 if (origData && origData->IsOrig()) {
2053 nsTableCellFrame* cellFrame = origData->GetCellFrame();
2054 if (cellFrame) {
2055 // possible change the number of colums to iterate
2056 maxCols = std::min(aColIndex + cellFrame->GetColSpan(), maxCols);
2057 if (colX >= maxCols)
2058 break;
2059 }
2060 }
2061 }
2062 if (data->IsColSpan()) {
2063 colSpan++;
2064 if (data->IsZeroColSpan()) {
2065 aZeroColSpan = true;
2066 }
2067 }
2068 else {
2069 break;
2070 }
2071 }
2072 else break;
2073 }
2074 return colSpan;
2075 }
2077 int32_t
2078 nsCellMap::GetRowSpanForNewCell(nsTableCellFrame* aCellFrameToAdd,
2079 int32_t aRowIndex,
2080 bool& aIsZeroRowSpan) const
2081 {
2082 aIsZeroRowSpan = false;
2083 int32_t rowSpan = aCellFrameToAdd->GetRowSpan();
2084 if (0 == rowSpan) {
2085 // Use a min value of 2 for a zero rowspan to make computations easier
2086 // elsewhere. Zero rowspans are only content dependent!
2087 rowSpan = std::max(2, mContentRowCount - aRowIndex);
2088 aIsZeroRowSpan = true;
2089 }
2090 return rowSpan;
2091 }
2093 bool nsCellMap::HasMoreThanOneCell(int32_t aRowIndex) const
2094 {
2095 const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow);
2096 uint32_t maxColIndex = row.Length();
2097 uint32_t count = 0;
2098 uint32_t colIndex;
2099 for (colIndex = 0; colIndex < maxColIndex; colIndex++) {
2100 CellData* cellData = row[colIndex];
2101 if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan()))
2102 count++;
2103 if (count > 1)
2104 return true;
2105 }
2106 return false;
2107 }
2109 int32_t
2110 nsCellMap::GetNumCellsOriginatingInRow(int32_t aRowIndex) const
2111 {
2112 const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow);
2113 uint32_t count = 0;
2114 uint32_t maxColIndex = row.Length();
2115 uint32_t colIndex;
2116 for (colIndex = 0; colIndex < maxColIndex; colIndex++) {
2117 CellData* cellData = row[colIndex];
2118 if (cellData && cellData->IsOrig())
2119 count++;
2120 }
2121 return count;
2122 }
2124 int32_t nsCellMap::GetRowSpan(int32_t aRowIndex,
2125 int32_t aColIndex,
2126 bool aGetEffective) const
2127 {
2128 int32_t rowSpan = 1;
2129 int32_t rowCount = (aGetEffective) ? mContentRowCount : mRows.Length();
2130 int32_t rowX;
2131 for (rowX = aRowIndex + 1; rowX < rowCount; rowX++) {
2132 CellData* data = GetDataAt(rowX, aColIndex);
2133 if (data) {
2134 if (data->IsRowSpan()) {
2135 rowSpan++;
2136 }
2137 else {
2138 break;
2139 }
2140 }
2141 else break;
2142 }
2143 return rowSpan;
2144 }
2146 void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap,
2147 nsTableCellFrame& aCellFrame,
2148 int32_t aRowIndex,
2149 int32_t aColIndex,
2150 int32_t aRgFirstRowIndex,
2151 nsIntRect& aDamageArea)
2152 {
2153 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2154 uint32_t colX, rowX;
2156 // get the rowspan and colspan from the cell map since the content may have changed
2157 bool zeroColSpan;
2158 uint32_t numCols = aMap.GetColCount();
2159 int32_t rowSpan = GetRowSpan(aRowIndex, aColIndex, true);
2160 uint32_t colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan);
2161 uint32_t endRowIndex = aRowIndex + rowSpan - 1;
2162 uint32_t endColIndex = aColIndex + colSpan - 1;
2164 if (aMap.mTableFrame.HasZeroColSpans()) {
2165 aMap.mTableFrame.SetNeedColSpanExpansion(true);
2166 }
2168 // adjust the col counts due to the deleted cell before removing it
2169 for (colX = aColIndex; colX <= endColIndex; colX++) {
2170 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2171 if (colX == uint32_t(aColIndex)) {
2172 colInfo->mNumCellsOrig--;
2173 }
2174 else {
2175 colInfo->mNumCellsSpan--;
2176 }
2177 }
2179 // remove the deleted cell and cellData entries for it
2180 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
2181 CellDataArray& row = mRows[rowX];
2183 // endIndexForRow points at the first slot we don't want to clean up. This
2184 // makes the aColIndex == 0 case work right with our unsigned int colX.
2185 NS_ASSERTION(endColIndex + 1 <= row.Length(), "span beyond the row size!");
2186 uint32_t endIndexForRow = std::min(endColIndex + 1, row.Length());
2188 // Since endIndexForRow <= row.Length(), enough to compare aColIndex to it.
2189 if (uint32_t(aColIndex) < endIndexForRow) {
2190 for (colX = endIndexForRow; colX > uint32_t(aColIndex); colX--) {
2191 DestroyCellData(row[colX-1]);
2192 }
2193 row.RemoveElementsAt(aColIndex, endIndexForRow - aColIndex);
2194 }
2195 }
2197 numCols = aMap.GetColCount();
2199 // update the row and col info due to shifting
2200 for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) {
2201 CellDataArray& row = mRows[rowX];
2202 for (colX = aColIndex; colX < numCols - colSpan; colX++) {
2203 CellData* data = row.SafeElementAt(colX);
2204 if (data) {
2205 if (data->IsOrig()) {
2206 // a cell that gets moved to the left needs adjustment in its new location
2207 data->GetCellFrame()->SetColIndex(colX);
2208 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2209 colInfo->mNumCellsOrig++;
2210 // a cell that gets moved to the left needs adjustment in its old location
2211 colInfo = aMap.GetColInfoAt(colX + colSpan);
2212 if (colInfo) {
2213 colInfo->mNumCellsOrig--;
2214 }
2215 }
2217 else if (data->IsColSpan()) {
2218 // a cell that gets moved to the left needs adjustment
2219 // in its new location
2220 nsColInfo* colInfo = aMap.GetColInfoAt(colX);
2221 colInfo->mNumCellsSpan++;
2222 // a cell that gets moved to the left needs adjustment
2223 // in its old location
2224 colInfo = aMap.GetColInfoAt(colX + colSpan);
2225 if (colInfo) {
2226 colInfo->mNumCellsSpan--;
2227 }
2228 }
2229 }
2230 }
2231 }
2232 aMap.RemoveColsAtEnd();
2233 SetDamageArea(aColIndex, aRgFirstRowIndex + aRowIndex,
2234 std::max(0, aMap.GetColCount() - aColIndex - 1),
2235 1 + endRowIndex - aRowIndex, aDamageArea);
2236 }
2238 void
2239 nsCellMap::RebuildConsideringRows(nsTableCellMap& aMap,
2240 int32_t aStartRowIndex,
2241 nsTArray<nsTableRowFrame*>* aRowsToInsert,
2242 int32_t aNumRowsToRemove)
2243 {
2244 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2245 // copy the old cell map into a new array
2246 uint32_t numOrigRows = mRows.Length();
2247 nsTArray<CellDataArray> origRows;
2248 mRows.SwapElements(origRows);
2250 int32_t rowNumberChange;
2251 if (aRowsToInsert) {
2252 rowNumberChange = aRowsToInsert->Length();
2253 } else {
2254 rowNumberChange = -aNumRowsToRemove;
2255 }
2257 // adjust mContentRowCount based on the function arguments as they are known to
2258 // be real rows.
2259 mContentRowCount += rowNumberChange;
2260 NS_ASSERTION(mContentRowCount >= 0, "previous mContentRowCount was wrong");
2261 // mRows is empty now. Grow it to the size we expect it to have.
2262 if (mContentRowCount) {
2263 if (!Grow(aMap, mContentRowCount)) {
2264 // Bail, I guess... Not sure what else we can do here.
2265 return;
2266 }
2267 }
2269 // aStartRowIndex might be after all existing rows so we should limit the
2270 // copy to the amount of exisiting rows
2271 uint32_t copyEndRowIndex = std::min(numOrigRows, uint32_t(aStartRowIndex));
2273 // rowX keeps track of where we are in mRows while setting up the
2274 // new cellmap.
2275 uint32_t rowX = 0;
2276 nsIntRect damageArea;
2277 // put back the rows before the affected ones just as before. Note that we
2278 // can't just copy the old rows in bit-for-bit, because they might be
2279 // spanning out into the rows we're adding/removing.
2280 for ( ; rowX < copyEndRowIndex; rowX++) {
2281 const CellDataArray& row = origRows[rowX];
2282 uint32_t numCols = row.Length();
2283 for (uint32_t colX = 0; colX < numCols; colX++) {
2284 // put in the original cell from the cell map
2285 const CellData* data = row.ElementAt(colX);
2286 if (data && data->IsOrig()) {
2287 AppendCell(aMap, data->GetCellFrame(), rowX, false, 0, damageArea);
2288 }
2289 }
2290 }
2292 // Now handle the new rows being inserted, if any.
2293 uint32_t copyStartRowIndex;
2294 rowX = aStartRowIndex;
2295 if (aRowsToInsert) {
2296 // add in the new cells and create rows if necessary
2297 int32_t numNewRows = aRowsToInsert->Length();
2298 for (int32_t newRowX = 0; newRowX < numNewRows; newRowX++) {
2299 nsTableRowFrame* rFrame = aRowsToInsert->ElementAt(newRowX);
2300 nsIFrame* cFrame = rFrame->GetFirstPrincipalChild();
2301 while (cFrame) {
2302 nsTableCellFrame *cellFrame = do_QueryFrame(cFrame);
2303 if (cellFrame) {
2304 AppendCell(aMap, cellFrame, rowX, false, 0, damageArea);
2305 }
2306 cFrame = cFrame->GetNextSibling();
2307 }
2308 rowX++;
2309 }
2310 copyStartRowIndex = aStartRowIndex;
2311 }
2312 else {
2313 copyStartRowIndex = aStartRowIndex + aNumRowsToRemove;
2314 }
2316 // put back the rows after the affected ones just as before. Again, we can't
2317 // just copy the old bits because that would not handle the new rows spanning
2318 // out or our earlier old rows spanning through the damaged area.
2319 for (uint32_t copyRowX = copyStartRowIndex; copyRowX < numOrigRows;
2320 copyRowX++) {
2321 const CellDataArray& row = origRows[copyRowX];
2322 uint32_t numCols = row.Length();
2323 for (uint32_t colX = 0; colX < numCols; colX++) {
2324 // put in the original cell from the cell map
2325 CellData* data = row.ElementAt(colX);
2326 if (data && data->IsOrig()) {
2327 AppendCell(aMap, data->GetCellFrame(), rowX, false, 0, damageArea);
2328 }
2329 }
2330 rowX++;
2331 }
2333 // delete the old cell map. Now rowX no longer has anything to do with mRows
2334 for (rowX = 0; rowX < numOrigRows; rowX++) {
2335 CellDataArray& row = origRows[rowX];
2336 uint32_t len = row.Length();
2337 for (uint32_t colX = 0; colX < len; colX++) {
2338 DestroyCellData(row[colX]);
2339 }
2340 }
2341 }
2343 void
2344 nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap,
2345 int32_t aNumOrigCols,
2346 nsTArray<nsTableCellFrame*>* aCellFrames,
2347 int32_t aRowIndex,
2348 int32_t aColIndex,
2349 bool aInsert)
2350 {
2351 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2352 // copy the old cell map into a new array
2353 int32_t numOrigRows = mRows.Length();
2354 nsTArray<CellDataArray> origRows;
2355 mRows.SwapElements(origRows);
2357 int32_t numNewCells = (aCellFrames) ? aCellFrames->Length() : 0;
2359 // the new cells might extend the previous column number
2360 NS_ASSERTION(aNumOrigCols >= aColIndex, "Appending cells far beyond cellmap data?!");
2361 int32_t numCols = aInsert ? std::max(aNumOrigCols, aColIndex + 1) : aNumOrigCols;
2363 // build the new cell map. Hard to say what, if anything, we can preallocate
2364 // here... Should come back to that sometime, perhaps.
2365 int32_t rowX;
2366 nsIntRect damageArea;
2367 for (rowX = 0; rowX < numOrigRows; rowX++) {
2368 const CellDataArray& row = origRows[rowX];
2369 for (int32_t colX = 0; colX < numCols; colX++) {
2370 if ((rowX == aRowIndex) && (colX == aColIndex)) {
2371 if (aInsert) { // put in the new cells
2372 for (int32_t cellX = 0; cellX < numNewCells; cellX++) {
2373 nsTableCellFrame* cell = aCellFrames->ElementAt(cellX);
2374 if (cell) {
2375 AppendCell(aMap, cell, rowX, false, 0, damageArea);
2376 }
2377 }
2378 }
2379 else {
2380 continue; // do not put the deleted cell back
2381 }
2382 }
2383 // put in the original cell from the cell map
2384 CellData* data = row.SafeElementAt(colX);
2385 if (data && data->IsOrig()) {
2386 AppendCell(aMap, data->GetCellFrame(), rowX, false, 0, damageArea);
2387 }
2388 }
2389 }
2390 if (aInsert && numOrigRows <= aRowIndex) { // append the new cells below the last original row
2391 NS_ASSERTION (numOrigRows == aRowIndex, "Appending cells far beyond the last row");
2392 for (int32_t cellX = 0; cellX < numNewCells; cellX++) {
2393 nsTableCellFrame* cell = aCellFrames->ElementAt(cellX);
2394 if (cell) {
2395 AppendCell(aMap, cell, aRowIndex, false, 0, damageArea);
2396 }
2397 }
2398 }
2400 // delete the old cell map
2401 for (rowX = 0; rowX < numOrigRows; rowX++) {
2402 CellDataArray& row = origRows[rowX];
2403 uint32_t len = row.Length();
2404 for (uint32_t colX = 0; colX < len; colX++) {
2405 DestroyCellData(row.SafeElementAt(colX));
2406 }
2407 }
2408 // expand the cellmap to cover empty content rows
2409 if (mRows.Length() < uint32_t(mContentRowCount)) {
2410 Grow(aMap, mContentRowCount - mRows.Length());
2411 }
2413 }
2415 void nsCellMap::RemoveCell(nsTableCellMap& aMap,
2416 nsTableCellFrame* aCellFrame,
2417 int32_t aRowIndex,
2418 int32_t aRgFirstRowIndex,
2419 nsIntRect& aDamageArea)
2420 {
2421 uint32_t numRows = mRows.Length();
2422 if (uint32_t(aRowIndex) >= numRows) {
2423 NS_ERROR("bad arg in nsCellMap::RemoveCell");
2424 return;
2425 }
2426 int32_t numCols = aMap.GetColCount();
2428 // Now aRowIndex is guaranteed OK.
2430 // get the starting col index of the cell to remove
2431 int32_t startColIndex;
2432 for (startColIndex = 0; startColIndex < numCols; startColIndex++) {
2433 CellData* data = mRows[aRowIndex].SafeElementAt(startColIndex);
2434 if (data && (data->IsOrig()) && (aCellFrame == data->GetCellFrame())) {
2435 break; // we found the col index
2436 }
2437 }
2439 int32_t rowSpan = GetRowSpan(aRowIndex, startColIndex, false);
2440 // record whether removing the cells is going to cause complications due
2441 // to existing row spans, col spans or table sizing.
2442 bool spansCauseRebuild = CellsSpanInOrOut(aRowIndex,
2443 aRowIndex + rowSpan - 1,
2444 startColIndex, numCols - 1);
2445 // XXX if the cell has a col span to the end of the map, and the end has no originating
2446 // cells, we need to assume that this the only such cell, and rebuild so that there are
2447 // no extraneous cols at the end. The same is true for removing rows.
2448 if (!aCellFrame->GetRowSpan() || !aCellFrame->GetColSpan())
2449 spansCauseRebuild = true;
2451 if (spansCauseRebuild) {
2452 aMap.RebuildConsideringCells(this, nullptr, aRowIndex, startColIndex, false,
2453 aDamageArea);
2454 }
2455 else {
2456 ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex,
2457 aRgFirstRowIndex, aDamageArea);
2458 }
2459 }
2461 void nsCellMap::ExpandZeroColSpans(nsTableCellMap& aMap)
2462 {
2463 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2464 uint32_t numRows = mRows.Length();
2465 uint32_t numCols = aMap.GetColCount();
2466 uint32_t rowIndex, colIndex;
2468 for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
2469 for (colIndex = 0; colIndex < numCols; colIndex++) {
2470 CellData* data = mRows[rowIndex].SafeElementAt(colIndex);
2471 if (!data || !data->IsOrig())
2472 continue;
2473 nsTableCellFrame* cell = data->GetCellFrame();
2474 NS_ASSERTION(cell, "There has to be a cell");
2475 int32_t cellRowSpan = cell->GetRowSpan();
2476 int32_t cellColSpan = cell->GetColSpan();
2477 bool rowZeroSpan = (0 == cell->GetRowSpan());
2478 bool colZeroSpan = (0 == cell->GetColSpan());
2479 if (colZeroSpan) {
2480 aMap.mTableFrame.SetHasZeroColSpans(true);
2481 // do the expansion
2482 NS_ASSERTION(numRows > 0, "Bogus numRows");
2483 NS_ASSERTION(numCols > 0, "Bogus numCols");
2484 uint32_t endRowIndex = rowZeroSpan ? numRows - 1 :
2485 rowIndex + cellRowSpan - 1;
2486 uint32_t endColIndex = colZeroSpan ? numCols - 1 :
2487 colIndex + cellColSpan - 1;
2488 uint32_t colX, rowX;
2489 colX = colIndex + 1;
2490 while (colX <= endColIndex) {
2491 // look at columns from here to our colspan. For each one, check
2492 // the rows from here to our rowspan to make sure there is no
2493 // obstacle to marking that column as a zerospanned column; if there
2494 // isn't, mark it so
2495 for (rowX = rowIndex; rowX <= endRowIndex; rowX++) {
2496 CellData* oldData = GetDataAt(rowX, colX);
2497 if (oldData) {
2498 if (oldData->IsOrig()) {
2499 break; // something is in the way
2500 }
2501 if (oldData->IsRowSpan()) {
2502 if ((rowX - rowIndex) != oldData->GetRowSpanOffset()) {
2503 break;
2504 }
2505 }
2506 if (oldData->IsColSpan()) {
2507 if ((colX - colIndex) != oldData->GetColSpanOffset()) {
2508 break;
2509 }
2510 }
2511 }
2512 }
2513 if (endRowIndex >= rowX)
2514 break;// we hit something
2515 for (rowX = rowIndex; rowX <= endRowIndex; rowX++) {
2516 CellData* newData = AllocCellData(nullptr);
2517 if (!newData) return;
2519 newData->SetColSpanOffset(colX - colIndex);
2520 newData->SetZeroColSpan(true);
2522 if (rowX > rowIndex) {
2523 newData->SetRowSpanOffset(rowX - rowIndex);
2524 if (rowZeroSpan)
2525 newData->SetZeroRowSpan(true);
2526 }
2527 SetDataAt(aMap, *newData, rowX, colX);
2528 }
2529 colX++;
2530 } // while (colX <= endColIndex)
2531 } // if zerocolspan
2532 }
2533 }
2534 }
2535 #ifdef DEBUG
2536 void nsCellMap::Dump(bool aIsBorderCollapse) const
2537 {
2538 printf("\n ***** START GROUP CELL MAP DUMP ***** %p\n", (void*)this);
2539 nsTableRowGroupFrame* rg = GetRowGroup();
2540 const nsStyleDisplay* display = rg->StyleDisplay();
2541 switch (display->mDisplay) {
2542 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
2543 printf(" thead ");
2544 break;
2545 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
2546 printf(" tfoot ");
2547 break;
2548 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
2549 printf(" tbody ");
2550 break;
2551 default:
2552 printf("HUH? wrong display type on rowgroup");
2553 }
2554 uint32_t mapRowCount = mRows.Length();
2555 printf("mapRowCount=%u tableRowCount=%d\n", mapRowCount, mContentRowCount);
2558 uint32_t rowIndex, colIndex;
2559 for (rowIndex = 0; rowIndex < mapRowCount; rowIndex++) {
2560 const CellDataArray& row = mRows[rowIndex];
2561 printf(" row %d : ", rowIndex);
2562 uint32_t colCount = row.Length();
2563 for (colIndex = 0; colIndex < colCount; colIndex++) {
2564 CellData* cd = row[colIndex];
2565 if (cd) {
2566 if (cd->IsOrig()) {
2567 printf("C%d,%d ", rowIndex, colIndex);
2568 } else {
2569 if (cd->IsRowSpan()) {
2570 printf("R ");
2571 }
2572 if (cd->IsColSpan()) {
2573 printf("C ");
2574 }
2575 if (!(cd->IsRowSpan() && cd->IsColSpan())) {
2576 printf(" ");
2577 }
2578 printf(" ");
2579 }
2580 } else {
2581 printf("---- ");
2582 }
2583 }
2584 if (aIsBorderCollapse) {
2585 nscoord size;
2586 BCBorderOwner owner;
2587 mozilla::css::Side side;
2588 bool segStart;
2589 bool bevel;
2590 for (int32_t i = 0; i <= 2; i++) {
2591 printf("\n ");
2592 for (colIndex = 0; colIndex < colCount; colIndex++) {
2593 BCCellData* cd = (BCCellData *)row[colIndex];
2594 if (cd) {
2595 if (0 == i) {
2596 size = cd->mData.GetTopEdge(owner, segStart);
2597 printf("t=%d%d%d ", int32_t(size), owner, segStart);
2598 }
2599 else if (1 == i) {
2600 size = cd->mData.GetLeftEdge(owner, segStart);
2601 printf("l=%d%d%d ", int32_t(size), owner, segStart);
2602 }
2603 else {
2604 size = cd->mData.GetCorner(side, bevel);
2605 printf("c=%d%d%d ", int32_t(size), side, bevel);
2606 }
2607 }
2608 }
2609 }
2610 }
2611 printf("\n");
2612 }
2614 // output info mapping Ci,j to cell address
2615 uint32_t cellCount = 0;
2616 for (uint32_t rIndex = 0; rIndex < mapRowCount; rIndex++) {
2617 const CellDataArray& row = mRows[rIndex];
2618 uint32_t colCount = row.Length();
2619 printf(" ");
2620 for (colIndex = 0; colIndex < colCount; colIndex++) {
2621 CellData* cd = row[colIndex];
2622 if (cd) {
2623 if (cd->IsOrig()) {
2624 nsTableCellFrame* cellFrame = cd->GetCellFrame();
2625 int32_t cellFrameColIndex;
2626 cellFrame->GetColIndex(cellFrameColIndex);
2627 printf("C%d,%d=%p(%d) ", rIndex, colIndex, (void*)cellFrame,
2628 cellFrameColIndex);
2629 cellCount++;
2630 }
2631 }
2632 }
2633 printf("\n");
2634 }
2636 printf(" ***** END GROUP CELL MAP DUMP *****\n");
2637 }
2638 #endif
2640 CellData*
2641 nsCellMap::GetDataAt(int32_t aMapRowIndex,
2642 int32_t aColIndex) const
2643 {
2644 return
2645 mRows.SafeElementAt(aMapRowIndex, *sEmptyRow).SafeElementAt(aColIndex);
2646 }
2648 // only called if the cell at aMapRowIndex, aColIndex is null or dead
2649 // (the latter from ExpandZeroColSpans).
2650 void nsCellMap::SetDataAt(nsTableCellMap& aMap,
2651 CellData& aNewCell,
2652 int32_t aMapRowIndex,
2653 int32_t aColIndex)
2654 {
2655 NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch");
2656 if (uint32_t(aMapRowIndex) >= mRows.Length()) {
2657 NS_ERROR("SetDataAt called with row index > num rows");
2658 return;
2659 }
2661 CellDataArray& row = mRows[aMapRowIndex];
2663 // the table map may need cols added
2664 int32_t numColsToAdd = aColIndex + 1 - aMap.GetColCount();
2665 if (numColsToAdd > 0) {
2666 aMap.AddColsAtEnd(numColsToAdd);
2667 }
2668 // the row may need cols added
2669 numColsToAdd = aColIndex + 1 - row.Length();
2670 if (numColsToAdd > 0) {
2671 // XXXbz need to handle allocation failures.
2672 GrowRow(row, numColsToAdd);
2673 }
2675 DestroyCellData(row[aColIndex]);
2677 row.ReplaceElementsAt(aColIndex, 1, &aNewCell);
2678 // update the originating cell counts if cell originates in this row, col
2679 nsColInfo* colInfo = aMap.GetColInfoAt(aColIndex);
2680 if (colInfo) {
2681 if (aNewCell.IsOrig()) {
2682 colInfo->mNumCellsOrig++;
2683 }
2684 else if (aNewCell.IsColSpan()) {
2685 colInfo->mNumCellsSpan++;
2686 }
2687 }
2688 else NS_ERROR("SetDataAt called with col index > table map num cols");
2689 }
2691 nsTableCellFrame*
2692 nsCellMap::GetCellInfoAt(const nsTableCellMap& aMap,
2693 int32_t aRowX,
2694 int32_t aColX,
2695 bool* aOriginates,
2696 int32_t* aColSpan) const
2697 {
2698 if (aOriginates) {
2699 *aOriginates = false;
2700 }
2701 CellData* data = GetDataAt(aRowX, aColX);
2702 nsTableCellFrame* cellFrame = nullptr;
2703 if (data) {
2704 if (data->IsOrig()) {
2705 cellFrame = data->GetCellFrame();
2706 if (aOriginates)
2707 *aOriginates = true;
2708 }
2709 else {
2710 cellFrame = GetCellFrame(aRowX, aColX, *data, true);
2711 }
2712 if (cellFrame && aColSpan) {
2713 int32_t initialColIndex;
2714 cellFrame->GetColIndex(initialColIndex);
2715 bool zeroSpan;
2716 *aColSpan = GetEffectiveColSpan(aMap, aRowX, initialColIndex, zeroSpan);
2717 }
2718 }
2719 return cellFrame;
2720 }
2723 bool nsCellMap::RowIsSpannedInto(int32_t aRowIndex,
2724 int32_t aNumEffCols) const
2725 {
2726 if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) {
2727 return false;
2728 }
2729 for (int32_t colIndex = 0; colIndex < aNumEffCols; colIndex++) {
2730 CellData* cd = GetDataAt(aRowIndex, colIndex);
2731 if (cd) { // there's really a cell at (aRowIndex, colIndex)
2732 if (cd->IsSpan()) { // the cell at (aRowIndex, colIndex) is the result of a span
2733 if (cd->IsRowSpan() && GetCellFrame(aRowIndex, colIndex, *cd, true)) { // XXX why the last check
2734 return true;
2735 }
2736 }
2737 }
2738 }
2739 return false;
2740 }
2742 bool nsCellMap::RowHasSpanningCells(int32_t aRowIndex,
2743 int32_t aNumEffCols) const
2744 {
2745 if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) {
2746 return false;
2747 }
2748 if (aRowIndex != mContentRowCount - 1) {
2749 // aRowIndex is not the last row, so we check the next row after aRowIndex for spanners
2750 for (int32_t colIndex = 0; colIndex < aNumEffCols; colIndex++) {
2751 CellData* cd = GetDataAt(aRowIndex, colIndex);
2752 if (cd && (cd->IsOrig())) { // cell originates
2753 CellData* cd2 = GetDataAt(aRowIndex + 1, colIndex);
2754 if (cd2 && cd2->IsRowSpan()) { // cd2 is spanned by a row
2755 if (cd->GetCellFrame() == GetCellFrame(aRowIndex + 1, colIndex, *cd2, true)) {
2756 return true;
2757 }
2758 }
2759 }
2760 }
2761 }
2762 return false;
2763 }
2765 void nsCellMap::DestroyCellData(CellData* aData)
2766 {
2767 if (!aData) {
2768 return;
2769 }
2771 if (mIsBC) {
2772 BCCellData* bcData = static_cast<BCCellData*>(aData);
2773 bcData->~BCCellData();
2774 mPresContext->FreeToShell(sizeof(BCCellData), bcData);
2775 } else {
2776 aData->~CellData();
2777 mPresContext->FreeToShell(sizeof(CellData), aData);
2778 }
2779 }
2781 CellData* nsCellMap::AllocCellData(nsTableCellFrame* aOrigCell)
2782 {
2783 if (mIsBC) {
2784 BCCellData* data = (BCCellData*)
2785 mPresContext->AllocateFromShell(sizeof(BCCellData));
2786 if (data) {
2787 new (data) BCCellData(aOrigCell);
2788 }
2789 return data;
2790 }
2792 CellData* data = (CellData*)
2793 mPresContext->AllocateFromShell(sizeof(CellData));
2794 if (data) {
2795 new (data) CellData(aOrigCell);
2796 }
2797 return data;
2798 }
2800 void
2801 nsCellMapColumnIterator::AdvanceRowGroup()
2802 {
2803 do {
2804 mCurMapStart += mCurMapContentRowCount;
2805 mCurMap = mCurMap->GetNextSibling();
2806 if (!mCurMap) {
2807 // Set mCurMapContentRowCount and mCurMapRelevantRowCount to 0 in case
2808 // mCurMap has no next sibling. This can happen if we just handled the
2809 // last originating cell. Future calls will end up with mFoundCells ==
2810 // mOrigCells, but for this one mFoundCells was definitely not big enough
2811 // if we got here.
2812 mCurMapContentRowCount = 0;
2813 mCurMapRelevantRowCount = 0;
2814 break;
2815 }
2817 mCurMapContentRowCount = mCurMap->GetRowCount();
2818 uint32_t rowArrayLength = mCurMap->mRows.Length();
2819 mCurMapRelevantRowCount = std::min(mCurMapContentRowCount, rowArrayLength);
2820 } while (0 == mCurMapRelevantRowCount);
2822 NS_ASSERTION(mCurMapRelevantRowCount != 0 || !mCurMap,
2823 "How did that happen?");
2825 // Set mCurMapRow to 0, since cells can't span across table row groups.
2826 mCurMapRow = 0;
2827 }
2829 void
2830 nsCellMapColumnIterator::IncrementRow(int32_t aIncrement)
2831 {
2832 NS_PRECONDITION(aIncrement >= 0, "Bogus increment");
2833 NS_PRECONDITION(mCurMap, "Bogus mOrigCells?");
2834 if (aIncrement == 0) {
2835 AdvanceRowGroup();
2836 }
2837 else {
2838 mCurMapRow += aIncrement;
2839 if (mCurMapRow >= mCurMapRelevantRowCount) {
2840 AdvanceRowGroup();
2841 }
2842 }
2843 }
2845 nsTableCellFrame*
2846 nsCellMapColumnIterator::GetNextFrame(int32_t* aRow, int32_t* aColSpan)
2847 {
2848 // Fast-path for the case when we don't have anything left in the column and
2849 // we know it.
2850 if (mFoundCells == mOrigCells) {
2851 *aRow = 0;
2852 *aColSpan = 1;
2853 return nullptr;
2854 }
2856 while (1) {
2857 NS_ASSERTION(mCurMapRow < mCurMapRelevantRowCount, "Bogus mOrigCells?");
2858 // Safe to just get the row (which is faster than calling GetDataAt(), but
2859 // there may not be that many cells in it, so have to use SafeElementAt for
2860 // the mCol.
2861 const nsCellMap::CellDataArray& row = mCurMap->mRows[mCurMapRow];
2862 CellData* cellData = row.SafeElementAt(mCol);
2863 if (!cellData || cellData->IsDead()) {
2864 // Could hit this if there are fewer cells in this row than others, for
2865 // example.
2866 IncrementRow(1);
2867 continue;
2868 }
2870 if (cellData->IsColSpan()) {
2871 // Look up the originating data for this cell, advance by its relative rowspan.
2872 int32_t rowspanOffset = cellData->GetRowSpanOffset();
2873 nsTableCellFrame* cellFrame = mCurMap->GetCellFrame(mCurMapRow, mCol, *cellData, false);
2874 NS_ASSERTION(cellFrame,"Must have usable originating data here");
2875 int32_t rowSpan = cellFrame->GetRowSpan();
2876 if (rowSpan == 0) {
2877 AdvanceRowGroup();
2878 }
2879 else {
2880 IncrementRow(rowSpan - rowspanOffset);
2881 }
2882 continue;
2883 }
2885 NS_ASSERTION(cellData->IsOrig(),
2886 "Must have originating cellData by this point. "
2887 "See comment on mCurMapRow in header.");
2889 nsTableCellFrame* cellFrame = cellData->GetCellFrame();
2890 NS_ASSERTION(cellFrame, "Orig data without cellframe?");
2892 *aRow = mCurMapStart + mCurMapRow;
2893 bool ignoredZeroSpan;
2894 *aColSpan = mCurMap->GetEffectiveColSpan(*mMap, mCurMapRow, mCol,
2895 ignoredZeroSpan);
2897 IncrementRow(cellFrame->GetRowSpan());
2899 ++mFoundCells;
2901 NS_ABORT_IF_FALSE(cellData == mMap->GetDataAt(*aRow, mCol),
2902 "Giving caller bogus row?");
2904 return cellFrame;
2905 }
2907 NS_NOTREACHED("Can't get here");
2908 return nullptr;
2909 }