tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }