layout/tables/nsCellMap.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial