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