layout/tables/nsCellMap.cpp

Wed, 31 Dec 2014 13:27:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 13:27:57 +0100
branch
TOR_BUG_3246
changeset 6
8bccb770b82d
permissions
-rw-r--r--

Ignore runtime configuration files generated during quality assurance.

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

mercurial