Coverage Report

Created: 2018-09-25 14:53

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