1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/tables/nsCellMap.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,2909 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsTArray.h" 1.10 +#include "nsCellMap.h" 1.11 +#include "nsTableFrame.h" 1.12 +#include "nsTableCellFrame.h" 1.13 +#include "nsTableRowFrame.h" 1.14 +#include "nsTableRowGroupFrame.h" 1.15 +#include <algorithm> 1.16 + 1.17 + 1.18 +static void 1.19 +SetDamageArea(int32_t aXOrigin, 1.20 + int32_t aYOrigin, 1.21 + int32_t aWidth, 1.22 + int32_t aHeight, 1.23 + nsIntRect& aDamageArea) 1.24 +{ 1.25 + NS_ASSERTION(aXOrigin >= 0, "negative col index"); 1.26 + NS_ASSERTION(aYOrigin >= 0, "negative row index"); 1.27 + NS_ASSERTION(aWidth >= 0, "negative horizontal damage"); 1.28 + NS_ASSERTION(aHeight >= 0, "negative vertical damage"); 1.29 + aDamageArea.x = aXOrigin; 1.30 + aDamageArea.y = aYOrigin; 1.31 + aDamageArea.width = aWidth; 1.32 + aDamageArea.height = aHeight; 1.33 +} 1.34 + 1.35 +// Empty static array used for SafeElementAt() calls on mRows. 1.36 +static nsCellMap::CellDataArray * sEmptyRow; 1.37 + 1.38 +// CellData 1.39 + 1.40 +CellData::CellData(nsTableCellFrame* aOrigCell) 1.41 +{ 1.42 + MOZ_COUNT_CTOR(CellData); 1.43 + static_assert(sizeof(mOrigCell) == sizeof(mBits), 1.44 + "mOrigCell and mBits must be the same size"); 1.45 + mOrigCell = aOrigCell; 1.46 +} 1.47 + 1.48 +CellData::~CellData() 1.49 +{ 1.50 + MOZ_COUNT_DTOR(CellData); 1.51 +} 1.52 + 1.53 +BCCellData::BCCellData(nsTableCellFrame* aOrigCell) 1.54 +:CellData(aOrigCell) 1.55 +{ 1.56 + MOZ_COUNT_CTOR(BCCellData); 1.57 +} 1.58 + 1.59 +BCCellData::~BCCellData() 1.60 +{ 1.61 + MOZ_COUNT_DTOR(BCCellData); 1.62 +} 1.63 + 1.64 +// nsTableCellMap 1.65 + 1.66 +nsTableCellMap::nsTableCellMap(nsTableFrame& aTableFrame, 1.67 + bool aBorderCollapse) 1.68 +:mTableFrame(aTableFrame), mFirstMap(nullptr), mBCInfo(nullptr) 1.69 +{ 1.70 + MOZ_COUNT_CTOR(nsTableCellMap); 1.71 + 1.72 + nsTableFrame::RowGroupArray orderedRowGroups; 1.73 + aTableFrame.OrderRowGroups(orderedRowGroups); 1.74 + 1.75 + nsTableRowGroupFrame* prior = nullptr; 1.76 + for (uint32_t rgX = 0; rgX < orderedRowGroups.Length(); rgX++) { 1.77 + nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX]; 1.78 + InsertGroupCellMap(rgFrame, prior); 1.79 + prior = rgFrame; 1.80 + } 1.81 + if (aBorderCollapse) { 1.82 + mBCInfo = new BCInfo(); 1.83 + } 1.84 +} 1.85 + 1.86 +nsTableCellMap::~nsTableCellMap() 1.87 +{ 1.88 + MOZ_COUNT_DTOR(nsTableCellMap); 1.89 + 1.90 + nsCellMap* cellMap = mFirstMap; 1.91 + while (cellMap) { 1.92 + nsCellMap* next = cellMap->GetNextSibling(); 1.93 + delete cellMap; 1.94 + cellMap = next; 1.95 + } 1.96 + 1.97 + if (mBCInfo) { 1.98 + DeleteRightBottomBorders(); 1.99 + delete mBCInfo; 1.100 + } 1.101 +} 1.102 + 1.103 +// Get the bcData holding the border segments of the right edge of the table 1.104 +BCData* 1.105 +nsTableCellMap::GetRightMostBorder(int32_t aRowIndex) 1.106 +{ 1.107 + if (!mBCInfo) ABORT1(nullptr); 1.108 + 1.109 + int32_t numRows = mBCInfo->mRightBorders.Length(); 1.110 + if (aRowIndex < numRows) { 1.111 + return &mBCInfo->mRightBorders.ElementAt(aRowIndex); 1.112 + } 1.113 + 1.114 + mBCInfo->mRightBorders.SetLength(aRowIndex+1); 1.115 + return &mBCInfo->mRightBorders.ElementAt(aRowIndex); 1.116 +} 1.117 + 1.118 +// Get the bcData holding the border segments of the bottom edge of the table 1.119 +BCData* 1.120 +nsTableCellMap::GetBottomMostBorder(int32_t aColIndex) 1.121 +{ 1.122 + if (!mBCInfo) ABORT1(nullptr); 1.123 + 1.124 + int32_t numCols = mBCInfo->mBottomBorders.Length(); 1.125 + if (aColIndex < numCols) { 1.126 + return &mBCInfo->mBottomBorders.ElementAt(aColIndex); 1.127 + } 1.128 + 1.129 + mBCInfo->mBottomBorders.SetLength(aColIndex+1); 1.130 + return &mBCInfo->mBottomBorders.ElementAt(aColIndex); 1.131 +} 1.132 + 1.133 +// delete the borders corresponding to the right and bottom edges of the table 1.134 +void 1.135 +nsTableCellMap::DeleteRightBottomBorders() 1.136 +{ 1.137 + if (mBCInfo) { 1.138 + mBCInfo->mBottomBorders.Clear(); 1.139 + mBCInfo->mRightBorders.Clear(); 1.140 + } 1.141 +} 1.142 + 1.143 +void 1.144 +nsTableCellMap::InsertGroupCellMap(nsCellMap* aPrevMap, 1.145 + nsCellMap& aNewMap) 1.146 +{ 1.147 + nsCellMap* next; 1.148 + if (aPrevMap) { 1.149 + next = aPrevMap->GetNextSibling(); 1.150 + aPrevMap->SetNextSibling(&aNewMap); 1.151 + } 1.152 + else { 1.153 + next = mFirstMap; 1.154 + mFirstMap = &aNewMap; 1.155 + } 1.156 + aNewMap.SetNextSibling(next); 1.157 +} 1.158 + 1.159 +void nsTableCellMap::InsertGroupCellMap(nsTableRowGroupFrame* aNewGroup, 1.160 + nsTableRowGroupFrame*& aPrevGroup) 1.161 +{ 1.162 + nsCellMap* newMap = new nsCellMap(aNewGroup, mBCInfo != nullptr); 1.163 + nsCellMap* prevMap = nullptr; 1.164 + nsCellMap* lastMap = mFirstMap; 1.165 + if (aPrevGroup) { 1.166 + nsCellMap* map = mFirstMap; 1.167 + while (map) { 1.168 + lastMap = map; 1.169 + if (map->GetRowGroup() == aPrevGroup) { 1.170 + prevMap = map; 1.171 + break; 1.172 + } 1.173 + map = map->GetNextSibling(); 1.174 + } 1.175 + } 1.176 + if (!prevMap) { 1.177 + if (aPrevGroup) { 1.178 + prevMap = lastMap; 1.179 + aPrevGroup = (prevMap) ? prevMap->GetRowGroup() : nullptr; 1.180 + } 1.181 + else { 1.182 + aPrevGroup = nullptr; 1.183 + } 1.184 + } 1.185 + InsertGroupCellMap(prevMap, *newMap); 1.186 +} 1.187 + 1.188 +void nsTableCellMap::RemoveGroupCellMap(nsTableRowGroupFrame* aGroup) 1.189 +{ 1.190 + nsCellMap* map = mFirstMap; 1.191 + nsCellMap* prior = nullptr; 1.192 + while (map) { 1.193 + if (map->GetRowGroup() == aGroup) { 1.194 + nsCellMap* next = map->GetNextSibling(); 1.195 + if (mFirstMap == map) { 1.196 + mFirstMap = next; 1.197 + } 1.198 + else { 1.199 + prior->SetNextSibling(next); 1.200 + } 1.201 + delete map; 1.202 + break; 1.203 + } 1.204 + prior = map; 1.205 + map = map->GetNextSibling(); 1.206 + } 1.207 +} 1.208 + 1.209 +static nsCellMap* 1.210 +FindMapFor(const nsTableRowGroupFrame* aRowGroup, 1.211 + nsCellMap* aStart, 1.212 + const nsCellMap* aEnd) 1.213 +{ 1.214 + for (nsCellMap* map = aStart; map != aEnd; map = map->GetNextSibling()) { 1.215 + if (aRowGroup == map->GetRowGroup()) { 1.216 + return map; 1.217 + } 1.218 + } 1.219 + 1.220 + return nullptr; 1.221 +} 1.222 + 1.223 +nsCellMap* 1.224 +nsTableCellMap::GetMapFor(const nsTableRowGroupFrame* aRowGroup, 1.225 + nsCellMap* aStartHint) const 1.226 +{ 1.227 + NS_PRECONDITION(aRowGroup, "Must have a rowgroup"); 1.228 + NS_ASSERTION(!aRowGroup->GetPrevInFlow(), "GetMapFor called with continuation"); 1.229 + if (aStartHint) { 1.230 + nsCellMap* map = FindMapFor(aRowGroup, aStartHint, nullptr); 1.231 + if (map) { 1.232 + return map; 1.233 + } 1.234 + } 1.235 + 1.236 + nsCellMap* map = FindMapFor(aRowGroup, mFirstMap, aStartHint); 1.237 + if (map) { 1.238 + return map; 1.239 + } 1.240 + 1.241 + // if aRowGroup is a repeated header or footer find the header or footer it was repeated from 1.242 + if (aRowGroup->IsRepeatable()) { 1.243 + nsTableFrame* fifTable = static_cast<nsTableFrame*>(mTableFrame.FirstInFlow()); 1.244 + 1.245 + const nsStyleDisplay* display = aRowGroup->StyleDisplay(); 1.246 + nsTableRowGroupFrame* rgOrig = 1.247 + (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay) ? 1.248 + fifTable->GetTHead() : fifTable->GetTFoot(); 1.249 + // find the row group cell map using the original header/footer 1.250 + if (rgOrig && rgOrig != aRowGroup) { 1.251 + return GetMapFor(rgOrig, aStartHint); 1.252 + } 1.253 + } 1.254 + 1.255 + return nullptr; 1.256 +} 1.257 + 1.258 +void 1.259 +nsTableCellMap::Synchronize(nsTableFrame* aTableFrame) 1.260 +{ 1.261 + nsTableFrame::RowGroupArray orderedRowGroups; 1.262 + nsAutoTArray<nsCellMap*, 8> maps; 1.263 + 1.264 + aTableFrame->OrderRowGroups(orderedRowGroups); 1.265 + if (!orderedRowGroups.Length()) { 1.266 + return; 1.267 + } 1.268 + 1.269 + // XXXbz this fails if orderedRowGroups is missing some row groups 1.270 + // (due to OOM when appending to the array, e.g. -- we leak maps in 1.271 + // that case). 1.272 + 1.273 + // Scope |map| outside the loop so we can use it as a hint. 1.274 + nsCellMap* map = nullptr; 1.275 + for (uint32_t rgX = 0; rgX < orderedRowGroups.Length(); rgX++) { 1.276 + nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgX]; 1.277 + map = GetMapFor(static_cast<nsTableRowGroupFrame*>(rgFrame->FirstInFlow()), 1.278 + map); 1.279 + if (map) { 1.280 + if (!maps.AppendElement(map)) { 1.281 + delete map; 1.282 + map = nullptr; 1.283 + NS_WARNING("Could not AppendElement"); 1.284 + break; 1.285 + } 1.286 + } 1.287 + } 1.288 + if (maps.IsEmpty()) { 1.289 + MOZ_ASSERT(!mFirstMap); 1.290 + return; 1.291 + } 1.292 + 1.293 + int32_t mapIndex = maps.Length() - 1; // Might end up -1 1.294 + nsCellMap* nextMap = maps.ElementAt(mapIndex); 1.295 + nextMap->SetNextSibling(nullptr); 1.296 + for (mapIndex-- ; mapIndex >= 0; mapIndex--) { 1.297 + nsCellMap* map = maps.ElementAt(mapIndex); 1.298 + map->SetNextSibling(nextMap); 1.299 + nextMap = map; 1.300 + } 1.301 + mFirstMap = nextMap; 1.302 +} 1.303 + 1.304 +bool 1.305 +nsTableCellMap::HasMoreThanOneCell(int32_t aRowIndex) const 1.306 +{ 1.307 + int32_t rowIndex = aRowIndex; 1.308 + nsCellMap* map = mFirstMap; 1.309 + while (map) { 1.310 + if (map->GetRowCount() > rowIndex) { 1.311 + return map->HasMoreThanOneCell(rowIndex); 1.312 + } 1.313 + rowIndex -= map->GetRowCount(); 1.314 + map = map->GetNextSibling(); 1.315 + } 1.316 + return false; 1.317 +} 1.318 + 1.319 +int32_t 1.320 +nsTableCellMap::GetNumCellsOriginatingInRow(int32_t aRowIndex) const 1.321 +{ 1.322 + int32_t rowIndex = aRowIndex; 1.323 + nsCellMap* map = mFirstMap; 1.324 + while (map) { 1.325 + if (map->GetRowCount() > rowIndex) { 1.326 + return map->GetNumCellsOriginatingInRow(rowIndex); 1.327 + } 1.328 + rowIndex -= map->GetRowCount(); 1.329 + map = map->GetNextSibling(); 1.330 + } 1.331 + return 0; 1.332 +} 1.333 +int32_t 1.334 +nsTableCellMap::GetEffectiveRowSpan(int32_t aRowIndex, 1.335 + int32_t aColIndex) const 1.336 +{ 1.337 + int32_t rowIndex = aRowIndex; 1.338 + nsCellMap* map = mFirstMap; 1.339 + while (map) { 1.340 + if (map->GetRowCount() > rowIndex) { 1.341 + return map->GetRowSpan(rowIndex, aColIndex, true); 1.342 + } 1.343 + rowIndex -= map->GetRowCount(); 1.344 + map = map->GetNextSibling(); 1.345 + } 1.346 + NS_NOTREACHED("Bogus row index?"); 1.347 + return 0; 1.348 +} 1.349 + 1.350 +int32_t 1.351 +nsTableCellMap::GetEffectiveColSpan(int32_t aRowIndex, 1.352 + int32_t aColIndex) const 1.353 +{ 1.354 + int32_t rowIndex = aRowIndex; 1.355 + nsCellMap* map = mFirstMap; 1.356 + while (map) { 1.357 + if (map->GetRowCount() > rowIndex) { 1.358 + bool zeroColSpan; 1.359 + return map->GetEffectiveColSpan(*this, rowIndex, aColIndex, zeroColSpan); 1.360 + } 1.361 + rowIndex -= map->GetRowCount(); 1.362 + map = map->GetNextSibling(); 1.363 + } 1.364 + NS_NOTREACHED("Bogus row index?"); 1.365 + return 0; 1.366 +} 1.367 + 1.368 +nsTableCellFrame* 1.369 +nsTableCellMap::GetCellFrame(int32_t aRowIndex, 1.370 + int32_t aColIndex, 1.371 + CellData& aData, 1.372 + bool aUseRowIfOverlap) const 1.373 +{ 1.374 + int32_t rowIndex = aRowIndex; 1.375 + nsCellMap* map = mFirstMap; 1.376 + while (map) { 1.377 + if (map->GetRowCount() > rowIndex) { 1.378 + return map->GetCellFrame(rowIndex, aColIndex, aData, aUseRowIfOverlap); 1.379 + } 1.380 + rowIndex -= map->GetRowCount(); 1.381 + map = map->GetNextSibling(); 1.382 + } 1.383 + return nullptr; 1.384 +} 1.385 + 1.386 +nsColInfo* 1.387 +nsTableCellMap::GetColInfoAt(int32_t aColIndex) 1.388 +{ 1.389 + int32_t numColsToAdd = aColIndex + 1 - mCols.Length(); 1.390 + if (numColsToAdd > 0) { 1.391 + AddColsAtEnd(numColsToAdd); // XXX this could fail to add cols in theory 1.392 + } 1.393 + return &mCols.ElementAt(aColIndex); 1.394 +} 1.395 + 1.396 +int32_t 1.397 +nsTableCellMap::GetRowCount() const 1.398 +{ 1.399 + int32_t numRows = 0; 1.400 + nsCellMap* map = mFirstMap; 1.401 + while (map) { 1.402 + numRows += map->GetRowCount(); 1.403 + map = map->GetNextSibling(); 1.404 + } 1.405 + return numRows; 1.406 +} 1.407 + 1.408 +CellData* 1.409 +nsTableCellMap::GetDataAt(int32_t aRowIndex, 1.410 + int32_t aColIndex) const 1.411 +{ 1.412 + int32_t rowIndex = aRowIndex; 1.413 + nsCellMap* map = mFirstMap; 1.414 + while (map) { 1.415 + if (map->GetRowCount() > rowIndex) { 1.416 + return map->GetDataAt(rowIndex, aColIndex); 1.417 + } 1.418 + rowIndex -= map->GetRowCount(); 1.419 + map = map->GetNextSibling(); 1.420 + } 1.421 + return nullptr; 1.422 +} 1.423 + 1.424 +void 1.425 +nsTableCellMap::AddColsAtEnd(uint32_t aNumCols) 1.426 +{ 1.427 + if (!mCols.AppendElements(aNumCols)) { 1.428 + NS_WARNING("Could not AppendElement"); 1.429 + } 1.430 + if (mBCInfo) { 1.431 + if (!mBCInfo->mBottomBorders.AppendElements(aNumCols)) { 1.432 + NS_WARNING("Could not AppendElement"); 1.433 + } 1.434 + } 1.435 +} 1.436 + 1.437 +void 1.438 +nsTableCellMap::RemoveColsAtEnd() 1.439 +{ 1.440 + // Remove the cols at the end which don't have originating cells or cells spanning 1.441 + // into them. Only do this if the col was created as eColAnonymousCell 1.442 + int32_t numCols = GetColCount(); 1.443 + int32_t lastGoodColIndex = mTableFrame.GetIndexOfLastRealCol(); 1.444 + for (int32_t colX = numCols - 1; (colX >= 0) && (colX > lastGoodColIndex); colX--) { 1.445 + nsColInfo& colInfo = mCols.ElementAt(colX); 1.446 + if ((colInfo.mNumCellsOrig <= 0) && (colInfo.mNumCellsSpan <= 0)) { 1.447 + mCols.RemoveElementAt(colX); 1.448 + 1.449 + if (mBCInfo) { 1.450 + int32_t count = mBCInfo->mBottomBorders.Length(); 1.451 + if (colX < count) { 1.452 + mBCInfo->mBottomBorders.RemoveElementAt(colX); 1.453 + } 1.454 + } 1.455 + } 1.456 + else break; // only remove until we encounter the 1st valid one 1.457 + } 1.458 +} 1.459 + 1.460 +void 1.461 +nsTableCellMap::ClearCols() 1.462 +{ 1.463 + mCols.Clear(); 1.464 + if (mBCInfo) 1.465 + mBCInfo->mBottomBorders.Clear(); 1.466 +} 1.467 +void 1.468 +nsTableCellMap::InsertRows(nsTableRowGroupFrame* aParent, 1.469 + nsTArray<nsTableRowFrame*>& aRows, 1.470 + int32_t aFirstRowIndex, 1.471 + bool aConsiderSpans, 1.472 + nsIntRect& aDamageArea) 1.473 +{ 1.474 + int32_t numNewRows = aRows.Length(); 1.475 + if ((numNewRows <= 0) || (aFirstRowIndex < 0)) ABORT0(); 1.476 + 1.477 + int32_t rowIndex = aFirstRowIndex; 1.478 + int32_t rgStartRowIndex = 0; 1.479 + nsCellMap* cellMap = mFirstMap; 1.480 + while (cellMap) { 1.481 + nsTableRowGroupFrame* rg = cellMap->GetRowGroup(); 1.482 + if (rg == aParent) { 1.483 + cellMap->InsertRows(*this, aRows, rowIndex, aConsiderSpans, 1.484 + rgStartRowIndex, aDamageArea); 1.485 +#ifdef DEBUG_TABLE_CELLMAP 1.486 + Dump("after InsertRows"); 1.487 +#endif 1.488 + if (mBCInfo) { 1.489 + int32_t count = mBCInfo->mRightBorders.Length(); 1.490 + if (aFirstRowIndex < count) { 1.491 + for (int32_t rowX = aFirstRowIndex; rowX < aFirstRowIndex + numNewRows; rowX++) { 1.492 + mBCInfo->mRightBorders.InsertElementAt(rowX); 1.493 + } 1.494 + } 1.495 + else { 1.496 + GetRightMostBorder(aFirstRowIndex); // this will create missing entries 1.497 + for (int32_t rowX = aFirstRowIndex + 1; rowX < aFirstRowIndex + numNewRows; rowX++) { 1.498 + mBCInfo->mRightBorders.AppendElement(); 1.499 + } 1.500 + } 1.501 + } 1.502 + return; 1.503 + } 1.504 + int32_t rowCount = cellMap->GetRowCount(); 1.505 + rgStartRowIndex += rowCount; 1.506 + rowIndex -= rowCount; 1.507 + cellMap = cellMap->GetNextSibling(); 1.508 + } 1.509 + 1.510 + NS_ERROR("Attempt to insert row into wrong map."); 1.511 +} 1.512 + 1.513 +void 1.514 +nsTableCellMap::RemoveRows(int32_t aFirstRowIndex, 1.515 + int32_t aNumRowsToRemove, 1.516 + bool aConsiderSpans, 1.517 + nsIntRect& aDamageArea) 1.518 +{ 1.519 + int32_t rowIndex = aFirstRowIndex; 1.520 + int32_t rgStartRowIndex = 0; 1.521 + nsCellMap* cellMap = mFirstMap; 1.522 + while (cellMap) { 1.523 + int32_t rowCount = cellMap->GetRowCount(); 1.524 + if (rowCount > rowIndex) { 1.525 + cellMap->RemoveRows(*this, rowIndex, aNumRowsToRemove, aConsiderSpans, 1.526 + rgStartRowIndex, aDamageArea); 1.527 + if (mBCInfo) { 1.528 + for (int32_t rowX = aFirstRowIndex + aNumRowsToRemove - 1; rowX >= aFirstRowIndex; rowX--) { 1.529 + if (uint32_t(rowX) < mBCInfo->mRightBorders.Length()) { 1.530 + mBCInfo->mRightBorders.RemoveElementAt(rowX); 1.531 + } 1.532 + } 1.533 + } 1.534 + break; 1.535 + } 1.536 + rgStartRowIndex += rowCount; 1.537 + rowIndex -= rowCount; 1.538 + cellMap = cellMap->GetNextSibling(); 1.539 + } 1.540 +#ifdef DEBUG_TABLE_CELLMAP 1.541 + Dump("after RemoveRows"); 1.542 +#endif 1.543 +} 1.544 + 1.545 + 1.546 + 1.547 +CellData* 1.548 +nsTableCellMap::AppendCell(nsTableCellFrame& aCellFrame, 1.549 + int32_t aRowIndex, 1.550 + bool aRebuildIfNecessary, 1.551 + nsIntRect& aDamageArea) 1.552 +{ 1.553 + MOZ_ASSERT(&aCellFrame == aCellFrame.FirstInFlow(), 1.554 + "invalid call on continuing frame"); 1.555 + nsIFrame* rgFrame = aCellFrame.GetParent(); // get the row 1.556 + if (!rgFrame) return 0; 1.557 + rgFrame = rgFrame->GetParent(); // get the row group 1.558 + if (!rgFrame) return 0; 1.559 + 1.560 + CellData* result = nullptr; 1.561 + int32_t rowIndex = aRowIndex; 1.562 + int32_t rgStartRowIndex = 0; 1.563 + nsCellMap* cellMap = mFirstMap; 1.564 + while (cellMap) { 1.565 + if (cellMap->GetRowGroup() == rgFrame) { 1.566 + result = cellMap->AppendCell(*this, &aCellFrame, rowIndex, 1.567 + aRebuildIfNecessary, rgStartRowIndex, 1.568 + aDamageArea); 1.569 + break; 1.570 + } 1.571 + int32_t rowCount = cellMap->GetRowCount(); 1.572 + rgStartRowIndex += rowCount; 1.573 + rowIndex -= rowCount; 1.574 + cellMap = cellMap->GetNextSibling(); 1.575 + } 1.576 +#ifdef DEBUG_TABLE_CELLMAP 1.577 + Dump("after AppendCell"); 1.578 +#endif 1.579 + return result; 1.580 +} 1.581 + 1.582 + 1.583 +void 1.584 +nsTableCellMap::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames, 1.585 + int32_t aRowIndex, 1.586 + int32_t aColIndexBefore, 1.587 + nsIntRect& aDamageArea) 1.588 +{ 1.589 + int32_t rowIndex = aRowIndex; 1.590 + int32_t rgStartRowIndex = 0; 1.591 + nsCellMap* cellMap = mFirstMap; 1.592 + while (cellMap) { 1.593 + int32_t rowCount = cellMap->GetRowCount(); 1.594 + if (rowCount > rowIndex) { 1.595 + cellMap->InsertCells(*this, aCellFrames, rowIndex, aColIndexBefore, 1.596 + rgStartRowIndex, aDamageArea); 1.597 + break; 1.598 + } 1.599 + rgStartRowIndex += rowCount; 1.600 + rowIndex -= rowCount; 1.601 + cellMap = cellMap->GetNextSibling(); 1.602 + } 1.603 +#ifdef DEBUG_TABLE_CELLMAP 1.604 + Dump("after InsertCells"); 1.605 +#endif 1.606 +} 1.607 + 1.608 + 1.609 +void 1.610 +nsTableCellMap::RemoveCell(nsTableCellFrame* aCellFrame, 1.611 + int32_t aRowIndex, 1.612 + nsIntRect& aDamageArea) 1.613 +{ 1.614 + if (!aCellFrame) ABORT0(); 1.615 + MOZ_ASSERT(aCellFrame == aCellFrame->FirstInFlow(), 1.616 + "invalid call on continuing frame"); 1.617 + int32_t rowIndex = aRowIndex; 1.618 + int32_t rgStartRowIndex = 0; 1.619 + nsCellMap* cellMap = mFirstMap; 1.620 + while (cellMap) { 1.621 + int32_t rowCount = cellMap->GetRowCount(); 1.622 + if (rowCount > rowIndex) { 1.623 + cellMap->RemoveCell(*this, aCellFrame, rowIndex, rgStartRowIndex, 1.624 + aDamageArea); 1.625 +#ifdef DEBUG_TABLE_CELLMAP 1.626 + Dump("after RemoveCell"); 1.627 +#endif 1.628 + return; 1.629 + } 1.630 + rgStartRowIndex += rowCount; 1.631 + rowIndex -= rowCount; 1.632 + cellMap = cellMap->GetNextSibling(); 1.633 + } 1.634 + // if we reach this point - the cell did not get removed, the caller of this routine 1.635 + // will delete the cell and the cellmap will probably hold a reference to 1.636 + // the deleted cell which will cause a subsequent crash when this cell is 1.637 + // referenced later 1.638 + NS_ERROR("nsTableCellMap::RemoveCell - could not remove cell"); 1.639 +} 1.640 + 1.641 +void 1.642 +nsTableCellMap::RebuildConsideringCells(nsCellMap* aCellMap, 1.643 + nsTArray<nsTableCellFrame*>* aCellFrames, 1.644 + int32_t aRowIndex, 1.645 + int32_t aColIndex, 1.646 + bool aInsert, 1.647 + nsIntRect& aDamageArea) 1.648 +{ 1.649 + int32_t numOrigCols = GetColCount(); 1.650 + ClearCols(); 1.651 + nsCellMap* cellMap = mFirstMap; 1.652 + int32_t rowCount = 0; 1.653 + while (cellMap) { 1.654 + if (cellMap == aCellMap) { 1.655 + cellMap->RebuildConsideringCells(*this, numOrigCols, aCellFrames, 1.656 + aRowIndex, aColIndex, aInsert); 1.657 + } 1.658 + else { 1.659 + cellMap->RebuildConsideringCells(*this, numOrigCols, nullptr, -1, 0, 1.660 + false); 1.661 + } 1.662 + rowCount += cellMap->GetRowCount(); 1.663 + cellMap = cellMap->GetNextSibling(); 1.664 + } 1.665 + SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea); 1.666 +} 1.667 + 1.668 +void 1.669 +nsTableCellMap::RebuildConsideringRows(nsCellMap* aCellMap, 1.670 + int32_t aStartRowIndex, 1.671 + nsTArray<nsTableRowFrame*>* aRowsToInsert, 1.672 + int32_t aNumRowsToRemove, 1.673 + nsIntRect& aDamageArea) 1.674 +{ 1.675 + NS_PRECONDITION(!aRowsToInsert || aNumRowsToRemove == 0, 1.676 + "Can't handle both removing and inserting rows at once"); 1.677 + 1.678 + int32_t numOrigCols = GetColCount(); 1.679 + ClearCols(); 1.680 + nsCellMap* cellMap = mFirstMap; 1.681 + int32_t rowCount = 0; 1.682 + while (cellMap) { 1.683 + if (cellMap == aCellMap) { 1.684 + cellMap->RebuildConsideringRows(*this, aStartRowIndex, aRowsToInsert, 1.685 + aNumRowsToRemove); 1.686 + } 1.687 + else { 1.688 + cellMap->RebuildConsideringCells(*this, numOrigCols, nullptr, -1, 0, 1.689 + false); 1.690 + } 1.691 + rowCount += cellMap->GetRowCount(); 1.692 + cellMap = cellMap->GetNextSibling(); 1.693 + } 1.694 + SetDamageArea(0, 0, GetColCount(), rowCount, aDamageArea); 1.695 +} 1.696 + 1.697 +int32_t 1.698 +nsTableCellMap::GetNumCellsOriginatingInCol(int32_t aColIndex) const 1.699 +{ 1.700 + int32_t colCount = mCols.Length(); 1.701 + if ((aColIndex >= 0) && (aColIndex < colCount)) { 1.702 + return mCols.ElementAt(aColIndex).mNumCellsOrig; 1.703 + } 1.704 + else { 1.705 + NS_ERROR("nsCellMap::GetNumCellsOriginatingInCol - bad col index"); 1.706 + return 0; 1.707 + } 1.708 +} 1.709 + 1.710 +#ifdef DEBUG 1.711 +void 1.712 +nsTableCellMap::Dump(char* aString) const 1.713 +{ 1.714 + if (aString) 1.715 + printf("%s \n", aString); 1.716 + printf("***** START TABLE CELL MAP DUMP ***** %p\n", (void*)this); 1.717 + // output col info 1.718 + int32_t colCount = mCols.Length(); 1.719 + printf ("cols array orig/span-> %p", (void*)this); 1.720 + for (int32_t colX = 0; colX < colCount; colX++) { 1.721 + const nsColInfo& colInfo = mCols.ElementAt(colX); 1.722 + printf ("%d=%d/%d ", colX, colInfo.mNumCellsOrig, colInfo.mNumCellsSpan); 1.723 + } 1.724 + printf(" cols in cache %d\n", mTableFrame.GetColCache().Length()); 1.725 + nsCellMap* cellMap = mFirstMap; 1.726 + while (cellMap) { 1.727 + cellMap->Dump(nullptr != mBCInfo); 1.728 + cellMap = cellMap->GetNextSibling(); 1.729 + } 1.730 + if (nullptr != mBCInfo) { 1.731 + printf("***** bottom borders *****\n"); 1.732 + nscoord size; 1.733 + BCBorderOwner owner; 1.734 + mozilla::css::Side side; 1.735 + bool segStart; 1.736 + bool bevel; 1.737 + int32_t colIndex; 1.738 + int32_t numCols = mBCInfo->mBottomBorders.Length(); 1.739 + for (int32_t i = 0; i <= 2; i++) { 1.740 + 1.741 + printf("\n "); 1.742 + for (colIndex = 0; colIndex < numCols; colIndex++) { 1.743 + BCData& cd = mBCInfo->mBottomBorders.ElementAt(colIndex); 1.744 + if (0 == i) { 1.745 + size = cd.GetTopEdge(owner, segStart); 1.746 + printf("t=%d%X%d ", int32_t(size), owner, segStart); 1.747 + } 1.748 + else if (1 == i) { 1.749 + size = cd.GetLeftEdge(owner, segStart); 1.750 + printf("l=%d%X%d ", int32_t(size), owner, segStart); 1.751 + } 1.752 + else { 1.753 + size = cd.GetCorner(side, bevel); 1.754 + printf("c=%d%X%d ", int32_t(size), side, bevel); 1.755 + } 1.756 + } 1.757 + BCData& cd = mBCInfo->mLowerRightCorner; 1.758 + if (0 == i) { 1.759 + size = cd.GetTopEdge(owner, segStart); 1.760 + printf("t=%d%X%d ", int32_t(size), owner, segStart); 1.761 + } 1.762 + else if (1 == i) { 1.763 + size = cd.GetLeftEdge(owner, segStart); 1.764 + printf("l=%d%X%d ", int32_t(size), owner, segStart); 1.765 + } 1.766 + else { 1.767 + size = cd.GetCorner(side, bevel); 1.768 + printf("c=%d%X%d ", int32_t(size), side, bevel); 1.769 + } 1.770 + } 1.771 + printf("\n"); 1.772 + } 1.773 + printf("***** END TABLE CELL MAP DUMP *****\n"); 1.774 +} 1.775 +#endif 1.776 + 1.777 +nsTableCellFrame* 1.778 +nsTableCellMap::GetCellInfoAt(int32_t aRowIndex, 1.779 + int32_t aColIndex, 1.780 + bool* aOriginates, 1.781 + int32_t* aColSpan) const 1.782 +{ 1.783 + int32_t rowIndex = aRowIndex; 1.784 + nsCellMap* cellMap = mFirstMap; 1.785 + while (cellMap) { 1.786 + if (cellMap->GetRowCount() > rowIndex) { 1.787 + return cellMap->GetCellInfoAt(*this, rowIndex, aColIndex, aOriginates, aColSpan); 1.788 + } 1.789 + rowIndex -= cellMap->GetRowCount(); 1.790 + cellMap = cellMap->GetNextSibling(); 1.791 + } 1.792 + return nullptr; 1.793 +} 1.794 + 1.795 +int32_t 1.796 +nsTableCellMap::GetIndexByRowAndColumn(int32_t aRow, int32_t aColumn) const 1.797 +{ 1.798 + int32_t index = 0; 1.799 + 1.800 + int32_t colCount = mCols.Length(); 1.801 + int32_t rowIndex = aRow; 1.802 + 1.803 + nsCellMap* cellMap = mFirstMap; 1.804 + while (cellMap) { 1.805 + int32_t rowCount = cellMap->GetRowCount(); 1.806 + if (rowIndex >= rowCount) { 1.807 + // If the rowCount is less than the rowIndex, this means that the index is 1.808 + // not within the current map. If so, get the index of the last cell in 1.809 + // the last row. 1.810 + rowIndex -= rowCount; 1.811 + 1.812 + int32_t cellMapIdx = cellMap->GetHighestIndex(colCount); 1.813 + if (cellMapIdx != -1) 1.814 + index += cellMapIdx + 1; 1.815 + 1.816 + } else { 1.817 + // Index is in valid range for this cellmap, so get the index of rowIndex 1.818 + // and aColumn. 1.819 + int32_t cellMapIdx = cellMap->GetIndexByRowAndColumn(colCount, rowIndex, 1.820 + aColumn); 1.821 + if (cellMapIdx == -1) 1.822 + return -1; // no cell at the given row and column. 1.823 + 1.824 + index += cellMapIdx; 1.825 + return index; // no need to look through further maps here 1.826 + } 1.827 + 1.828 + cellMap = cellMap->GetNextSibling(); 1.829 + } 1.830 + 1.831 + return -1; 1.832 +} 1.833 + 1.834 +void 1.835 +nsTableCellMap::GetRowAndColumnByIndex(int32_t aIndex, 1.836 + int32_t *aRow, int32_t *aColumn) const 1.837 +{ 1.838 + *aRow = -1; 1.839 + *aColumn = -1; 1.840 + 1.841 + int32_t colCount = mCols.Length(); 1.842 + 1.843 + int32_t previousRows = 0; 1.844 + int32_t index = aIndex; 1.845 + 1.846 + nsCellMap* cellMap = mFirstMap; 1.847 + while (cellMap) { 1.848 + int32_t rowCount = cellMap->GetRowCount(); 1.849 + // Determine the highest possible index in this map to see 1.850 + // if wanted index is in here. 1.851 + int32_t cellMapIdx = cellMap->GetHighestIndex(colCount); 1.852 + if (cellMapIdx == -1) { 1.853 + // The index is not within this map, increase the total row index 1.854 + // accordingly. 1.855 + previousRows += rowCount; 1.856 + } else { 1.857 + if (index > cellMapIdx) { 1.858 + // The index is not within this map, so decrease it by the cellMapIdx 1.859 + // determined index and increase the total row index accordingly. 1.860 + index -= cellMapIdx + 1; 1.861 + previousRows += rowCount; 1.862 + } else { 1.863 + cellMap->GetRowAndColumnByIndex(colCount, index, aRow, aColumn); 1.864 + // If there were previous indexes, take them into account. 1.865 + *aRow += previousRows; 1.866 + return; // no need to look any further. 1.867 + } 1.868 + } 1.869 + 1.870 + cellMap = cellMap->GetNextSibling(); 1.871 + } 1.872 +} 1.873 + 1.874 +bool nsTableCellMap::RowIsSpannedInto(int32_t aRowIndex, 1.875 + int32_t aNumEffCols) const 1.876 +{ 1.877 + int32_t rowIndex = aRowIndex; 1.878 + nsCellMap* cellMap = mFirstMap; 1.879 + while (cellMap) { 1.880 + if (cellMap->GetRowCount() > rowIndex) { 1.881 + return cellMap->RowIsSpannedInto(rowIndex, aNumEffCols); 1.882 + } 1.883 + rowIndex -= cellMap->GetRowCount(); 1.884 + cellMap = cellMap->GetNextSibling(); 1.885 + } 1.886 + return false; 1.887 +} 1.888 + 1.889 +bool nsTableCellMap::RowHasSpanningCells(int32_t aRowIndex, 1.890 + int32_t aNumEffCols) const 1.891 +{ 1.892 + int32_t rowIndex = aRowIndex; 1.893 + nsCellMap* cellMap = mFirstMap; 1.894 + while (cellMap) { 1.895 + if (cellMap->GetRowCount() > rowIndex) { 1.896 + return cellMap->RowHasSpanningCells(rowIndex, aNumEffCols); 1.897 + } 1.898 + rowIndex -= cellMap->GetRowCount(); 1.899 + cellMap = cellMap->GetNextSibling(); 1.900 + } 1.901 + return false; 1.902 +} 1.903 + 1.904 +void nsTableCellMap::ExpandZeroColSpans() 1.905 +{ 1.906 + mTableFrame.SetNeedColSpanExpansion(false); // mark the work done 1.907 + mTableFrame.SetHasZeroColSpans(false); // reset the bit, if there is a 1.908 + // zerospan it will be set again. 1.909 + nsCellMap* cellMap = mFirstMap; 1.910 + while (cellMap) { 1.911 + cellMap->ExpandZeroColSpans(*this); 1.912 + cellMap = cellMap->GetNextSibling(); 1.913 + } 1.914 +} 1.915 + 1.916 +void 1.917 +nsTableCellMap::ResetTopStart(uint8_t aSide, 1.918 + nsCellMap& aCellMap, 1.919 + uint32_t aRowIndex, 1.920 + uint32_t aColIndex, 1.921 + bool aIsLowerRight) 1.922 +{ 1.923 + if (!mBCInfo || aIsLowerRight) ABORT0(); 1.924 + 1.925 + BCCellData* cellData; 1.926 + BCData* bcData = nullptr; 1.927 + 1.928 + switch(aSide) { 1.929 + case NS_SIDE_BOTTOM: 1.930 + aRowIndex++; 1.931 + // FALLTHROUGH 1.932 + case NS_SIDE_TOP: 1.933 + cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex); 1.934 + if (cellData) { 1.935 + bcData = &cellData->mData; 1.936 + } 1.937 + else { 1.938 + NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error"); 1.939 + // try the next row group 1.940 + nsCellMap* cellMap = aCellMap.GetNextSibling(); 1.941 + if (cellMap) { 1.942 + cellData = (BCCellData*)cellMap->GetDataAt(0, aColIndex); 1.943 + if (cellData) { 1.944 + bcData = &cellData->mData; 1.945 + } 1.946 + else { 1.947 + bcData = GetBottomMostBorder(aColIndex); 1.948 + } 1.949 + } 1.950 + } 1.951 + break; 1.952 + case NS_SIDE_RIGHT: 1.953 + aColIndex++; 1.954 + // FALLTHROUGH 1.955 + case NS_SIDE_LEFT: 1.956 + cellData = (BCCellData*)aCellMap.GetDataAt(aRowIndex, aColIndex); 1.957 + if (cellData) { 1.958 + bcData = &cellData->mData; 1.959 + } 1.960 + else { 1.961 + NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error"); 1.962 + bcData = GetRightMostBorder(aRowIndex); 1.963 + } 1.964 + break; 1.965 + } 1.966 + if (bcData) { 1.967 + bcData->SetTopStart(false); 1.968 + } 1.969 +} 1.970 + 1.971 +// store the aSide border segment at coord = (aRowIndex, aColIndex). For top/left, store 1.972 +// the info at coord. For bottom/left store it at the adjacent location so that it is 1.973 +// top/left at that location. If the new location is at the right or bottom edge of the 1.974 +// table, then store it one of the special arrays (right most borders, bottom most borders). 1.975 +void 1.976 +nsTableCellMap::SetBCBorderEdge(mozilla::css::Side aSide, 1.977 + nsCellMap& aCellMap, 1.978 + uint32_t aCellMapStart, 1.979 + uint32_t aRowIndex, 1.980 + uint32_t aColIndex, 1.981 + uint32_t aLength, 1.982 + BCBorderOwner aOwner, 1.983 + nscoord aSize, 1.984 + bool aChanged) 1.985 +{ 1.986 + if (!mBCInfo) ABORT0(); 1.987 + 1.988 + BCCellData* cellData; 1.989 + int32_t lastIndex, xIndex, yIndex; 1.990 + int32_t xPos = aColIndex; 1.991 + int32_t yPos = aRowIndex; 1.992 + int32_t rgYPos = aRowIndex - aCellMapStart; 1.993 + bool changed; 1.994 + 1.995 + switch(aSide) { 1.996 + case NS_SIDE_BOTTOM: 1.997 + rgYPos++; 1.998 + yPos++; 1.999 + case NS_SIDE_TOP: 1.1000 + lastIndex = xPos + aLength - 1; 1.1001 + for (xIndex = xPos; xIndex <= lastIndex; xIndex++) { 1.1002 + changed = aChanged && (xIndex == xPos); 1.1003 + BCData* bcData = nullptr; 1.1004 + cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xIndex); 1.1005 + if (!cellData) { 1.1006 + int32_t numRgRows = aCellMap.GetRowCount(); 1.1007 + if (yPos < numRgRows) { // add a dead cell data 1.1008 + nsIntRect damageArea; 1.1009 + cellData = (BCCellData*)aCellMap.AppendCell(*this, nullptr, rgYPos, 1.1010 + false, 0, damageArea); 1.1011 + if (!cellData) ABORT0(); 1.1012 + } 1.1013 + else { 1.1014 + NS_ASSERTION(aSide == NS_SIDE_BOTTOM, "program error"); 1.1015 + // try the next non empty row group 1.1016 + nsCellMap* cellMap = aCellMap.GetNextSibling(); 1.1017 + while (cellMap && (0 == cellMap->GetRowCount())) { 1.1018 + cellMap = cellMap->GetNextSibling(); 1.1019 + } 1.1020 + if (cellMap) { 1.1021 + cellData = (BCCellData*)cellMap->GetDataAt(0, xIndex); 1.1022 + if (!cellData) { // add a dead cell 1.1023 + nsIntRect damageArea; 1.1024 + cellData = (BCCellData*)cellMap->AppendCell(*this, nullptr, 0, 1.1025 + false, 0, 1.1026 + damageArea); 1.1027 + } 1.1028 + } 1.1029 + else { // must be at the end of the table 1.1030 + bcData = GetBottomMostBorder(xIndex); 1.1031 + } 1.1032 + } 1.1033 + } 1.1034 + if (!bcData && cellData) { 1.1035 + bcData = &cellData->mData; 1.1036 + } 1.1037 + if (bcData) { 1.1038 + bcData->SetTopEdge(aOwner, aSize, changed); 1.1039 + } 1.1040 + else NS_ERROR("Cellmap: Top edge not found"); 1.1041 + } 1.1042 + break; 1.1043 + case NS_SIDE_RIGHT: 1.1044 + xPos++; 1.1045 + case NS_SIDE_LEFT: 1.1046 + // since top, bottom borders were set, there should already be a cellData entry 1.1047 + lastIndex = rgYPos + aLength - 1; 1.1048 + for (yIndex = rgYPos; yIndex <= lastIndex; yIndex++) { 1.1049 + changed = aChanged && (yIndex == rgYPos); 1.1050 + cellData = (BCCellData*)aCellMap.GetDataAt(yIndex, xPos); 1.1051 + if (cellData) { 1.1052 + cellData->mData.SetLeftEdge(aOwner, aSize, changed); 1.1053 + } 1.1054 + else { 1.1055 + NS_ASSERTION(aSide == NS_SIDE_RIGHT, "program error"); 1.1056 + BCData* bcData = GetRightMostBorder(yIndex + aCellMapStart); 1.1057 + if (bcData) { 1.1058 + bcData->SetLeftEdge(aOwner, aSize, changed); 1.1059 + } 1.1060 + else NS_ERROR("Cellmap: Left edge not found"); 1.1061 + } 1.1062 + } 1.1063 + break; 1.1064 + } 1.1065 +} 1.1066 + 1.1067 +// store corner info (aOwner, aSubSize, aBevel). For aCorner = eTopLeft, store the info at 1.1068 +// (aRowIndex, aColIndex). For eTopRight, store it in the entry to the right where 1.1069 +// it would be top left. For eBottomRight, store it in the entry to the bottom. etc. 1.1070 +void 1.1071 +nsTableCellMap::SetBCBorderCorner(Corner aCorner, 1.1072 + nsCellMap& aCellMap, 1.1073 + uint32_t aCellMapStart, 1.1074 + uint32_t aRowIndex, 1.1075 + uint32_t aColIndex, 1.1076 + mozilla::css::Side aOwner, 1.1077 + nscoord aSubSize, 1.1078 + bool aBevel, 1.1079 + bool aIsBottomRight) 1.1080 +{ 1.1081 + if (!mBCInfo) ABORT0(); 1.1082 + 1.1083 + if (aIsBottomRight) { 1.1084 + mBCInfo->mLowerRightCorner.SetCorner(aSubSize, aOwner, aBevel); 1.1085 + return; 1.1086 + } 1.1087 + 1.1088 + int32_t xPos = aColIndex; 1.1089 + int32_t yPos = aRowIndex; 1.1090 + int32_t rgYPos = aRowIndex - aCellMapStart; 1.1091 + 1.1092 + if (eTopRight == aCorner) { 1.1093 + xPos++; 1.1094 + } 1.1095 + else if (eBottomRight == aCorner) { 1.1096 + xPos++; 1.1097 + rgYPos++; 1.1098 + yPos++; 1.1099 + } 1.1100 + else if (eBottomLeft == aCorner) { 1.1101 + rgYPos++; 1.1102 + yPos++; 1.1103 + } 1.1104 + 1.1105 + BCCellData* cellData = nullptr; 1.1106 + BCData* bcData = nullptr; 1.1107 + if (GetColCount() <= xPos) { 1.1108 + NS_ASSERTION(xPos == GetColCount(), "program error"); 1.1109 + // at the right edge of the table as we checked the corner before 1.1110 + NS_ASSERTION(!aIsBottomRight, "should be handled before"); 1.1111 + bcData = GetRightMostBorder(yPos); 1.1112 + } 1.1113 + else { 1.1114 + cellData = (BCCellData*)aCellMap.GetDataAt(rgYPos, xPos); 1.1115 + if (!cellData) { 1.1116 + int32_t numRgRows = aCellMap.GetRowCount(); 1.1117 + if (yPos < numRgRows) { // add a dead cell data 1.1118 + nsIntRect damageArea; 1.1119 + cellData = (BCCellData*)aCellMap.AppendCell(*this, nullptr, rgYPos, 1.1120 + false, 0, damageArea); 1.1121 + } 1.1122 + else { 1.1123 + // try the next non empty row group 1.1124 + nsCellMap* cellMap = aCellMap.GetNextSibling(); 1.1125 + while (cellMap && (0 == cellMap->GetRowCount())) { 1.1126 + cellMap = cellMap->GetNextSibling(); 1.1127 + } 1.1128 + if (cellMap) { 1.1129 + cellData = (BCCellData*)cellMap->GetDataAt(0, xPos); 1.1130 + if (!cellData) { // add a dead cell 1.1131 + nsIntRect damageArea; 1.1132 + cellData = (BCCellData*)cellMap->AppendCell(*this, nullptr, 0, 1.1133 + false, 0, damageArea); 1.1134 + } 1.1135 + } 1.1136 + else { // must be at the bottom of the table 1.1137 + bcData = GetBottomMostBorder(xPos); 1.1138 + } 1.1139 + } 1.1140 + } 1.1141 + } 1.1142 + if (!bcData && cellData) { 1.1143 + bcData = &cellData->mData; 1.1144 + } 1.1145 + if (bcData) { 1.1146 + bcData->SetCorner(aSubSize, aOwner, aBevel); 1.1147 + } 1.1148 + else NS_ERROR("program error: Corner not found"); 1.1149 +} 1.1150 + 1.1151 +nsCellMap::nsCellMap(nsTableRowGroupFrame* aRowGroup, bool aIsBC) 1.1152 + : mRows(8), mContentRowCount(0), mRowGroupFrame(aRowGroup), 1.1153 + mNextSibling(nullptr), mIsBC(aIsBC), 1.1154 + mPresContext(aRowGroup->PresContext()) 1.1155 +{ 1.1156 + MOZ_COUNT_CTOR(nsCellMap); 1.1157 + NS_ASSERTION(mPresContext, "Must have prescontext"); 1.1158 +} 1.1159 + 1.1160 +nsCellMap::~nsCellMap() 1.1161 +{ 1.1162 + MOZ_COUNT_DTOR(nsCellMap); 1.1163 + 1.1164 + uint32_t mapRowCount = mRows.Length(); 1.1165 + for (uint32_t rowX = 0; rowX < mapRowCount; rowX++) { 1.1166 + CellDataArray &row = mRows[rowX]; 1.1167 + uint32_t colCount = row.Length(); 1.1168 + for (uint32_t colX = 0; colX < colCount; colX++) { 1.1169 + DestroyCellData(row[colX]); 1.1170 + } 1.1171 + } 1.1172 +} 1.1173 + 1.1174 +/* static */ 1.1175 +void 1.1176 +nsCellMap::Init() 1.1177 +{ 1.1178 + NS_ABORT_IF_FALSE(!sEmptyRow, "How did that happen?"); 1.1179 + sEmptyRow = new nsCellMap::CellDataArray(); 1.1180 +} 1.1181 + 1.1182 +/* static */ 1.1183 +void 1.1184 +nsCellMap::Shutdown() 1.1185 +{ 1.1186 + delete sEmptyRow; 1.1187 + sEmptyRow = nullptr; 1.1188 +} 1.1189 + 1.1190 +nsTableCellFrame* 1.1191 +nsCellMap::GetCellFrame(int32_t aRowIndexIn, 1.1192 + int32_t aColIndexIn, 1.1193 + CellData& aData, 1.1194 + bool aUseRowIfOverlap) const 1.1195 +{ 1.1196 + int32_t rowIndex = aRowIndexIn - aData.GetRowSpanOffset(); 1.1197 + int32_t colIndex = aColIndexIn - aData.GetColSpanOffset(); 1.1198 + if (aData.IsOverlap()) { 1.1199 + if (aUseRowIfOverlap) { 1.1200 + colIndex = aColIndexIn; 1.1201 + } 1.1202 + else { 1.1203 + rowIndex = aRowIndexIn; 1.1204 + } 1.1205 + } 1.1206 + 1.1207 + CellData* data = 1.1208 + mRows.SafeElementAt(rowIndex, *sEmptyRow).SafeElementAt(colIndex); 1.1209 + if (data) { 1.1210 + return data->GetCellFrame(); 1.1211 + } 1.1212 + return nullptr; 1.1213 +} 1.1214 + 1.1215 +int32_t 1.1216 +nsCellMap::GetHighestIndex(int32_t aColCount) 1.1217 +{ 1.1218 + int32_t index = -1; 1.1219 + int32_t rowCount = mRows.Length(); 1.1220 + for (int32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { 1.1221 + const CellDataArray& row = mRows[rowIdx]; 1.1222 + 1.1223 + for (int32_t colIdx = 0; colIdx < aColCount; colIdx++) { 1.1224 + CellData* data = row.SafeElementAt(colIdx); 1.1225 + // No data means row doesn't have more cells. 1.1226 + if (!data) 1.1227 + break; 1.1228 + 1.1229 + if (data->IsOrig()) 1.1230 + index++; 1.1231 + } 1.1232 + } 1.1233 + 1.1234 + return index; 1.1235 +} 1.1236 + 1.1237 +int32_t 1.1238 +nsCellMap::GetIndexByRowAndColumn(int32_t aColCount, 1.1239 + int32_t aRow, int32_t aColumn) const 1.1240 +{ 1.1241 + if (uint32_t(aRow) >= mRows.Length()) 1.1242 + return -1; 1.1243 + 1.1244 + int32_t index = -1; 1.1245 + int32_t lastColsIdx = aColCount - 1; 1.1246 + 1.1247 + // Find row index of the cell where row span is started. 1.1248 + const CellDataArray& row = mRows[aRow]; 1.1249 + CellData* data = row.SafeElementAt(aColumn); 1.1250 + int32_t origRow = data ? aRow - data->GetRowSpanOffset() : aRow; 1.1251 + 1.1252 + // Calculate cell index. 1.1253 + for (int32_t rowIdx = 0; rowIdx <= origRow; rowIdx++) { 1.1254 + const CellDataArray& row = mRows[rowIdx]; 1.1255 + int32_t colCount = (rowIdx == origRow) ? aColumn : lastColsIdx; 1.1256 + 1.1257 + for (int32_t colIdx = 0; colIdx <= colCount; colIdx++) { 1.1258 + data = row.SafeElementAt(colIdx); 1.1259 + // No data means row doesn't have more cells. 1.1260 + if (!data) 1.1261 + break; 1.1262 + 1.1263 + if (data->IsOrig()) 1.1264 + index++; 1.1265 + } 1.1266 + } 1.1267 + 1.1268 + // Given row and column don't point to the cell. 1.1269 + if (!data) 1.1270 + return -1; 1.1271 + 1.1272 + return index; 1.1273 +} 1.1274 + 1.1275 +void 1.1276 +nsCellMap::GetRowAndColumnByIndex(int32_t aColCount, int32_t aIndex, 1.1277 + int32_t *aRow, int32_t *aColumn) const 1.1278 +{ 1.1279 + *aRow = -1; 1.1280 + *aColumn = -1; 1.1281 + 1.1282 + int32_t index = aIndex; 1.1283 + int32_t rowCount = mRows.Length(); 1.1284 + 1.1285 + for (int32_t rowIdx = 0; rowIdx < rowCount; rowIdx++) { 1.1286 + const CellDataArray& row = mRows[rowIdx]; 1.1287 + 1.1288 + for (int32_t colIdx = 0; colIdx < aColCount; colIdx++) { 1.1289 + CellData* data = row.SafeElementAt(colIdx); 1.1290 + 1.1291 + // The row doesn't have more cells. 1.1292 + if (!data) 1.1293 + break; 1.1294 + 1.1295 + if (data->IsOrig()) 1.1296 + index--; 1.1297 + 1.1298 + if (index < 0) { 1.1299 + *aRow = rowIdx; 1.1300 + *aColumn = colIdx; 1.1301 + return; 1.1302 + } 1.1303 + } 1.1304 + } 1.1305 +} 1.1306 + 1.1307 +bool nsCellMap::Grow(nsTableCellMap& aMap, 1.1308 + int32_t aNumRows, 1.1309 + int32_t aRowIndex) 1.1310 +{ 1.1311 + NS_ASSERTION(aNumRows >= 1, "Why are we calling this?"); 1.1312 + 1.1313 + // Get the number of cols we want to use for preallocating the row arrays. 1.1314 + int32_t numCols = aMap.GetColCount(); 1.1315 + if (numCols == 0) { 1.1316 + numCols = 4; 1.1317 + } 1.1318 + uint32_t startRowIndex = (aRowIndex >= 0) ? aRowIndex : mRows.Length(); 1.1319 + NS_ASSERTION(startRowIndex <= mRows.Length(), "Missing grow call inbetween"); 1.1320 + 1.1321 + return mRows.InsertElementsAt(startRowIndex, aNumRows, numCols) != nullptr; 1.1322 +} 1.1323 + 1.1324 +void nsCellMap::GrowRow(CellDataArray& aRow, 1.1325 + int32_t aNumCols) 1.1326 + 1.1327 +{ 1.1328 + // Have to have the cast to get the template to do the right thing. 1.1329 + aRow.InsertElementsAt(aRow.Length(), aNumCols, (CellData*)nullptr); 1.1330 +} 1.1331 + 1.1332 +void 1.1333 +nsCellMap::InsertRows(nsTableCellMap& aMap, 1.1334 + nsTArray<nsTableRowFrame*>& aRows, 1.1335 + int32_t aFirstRowIndex, 1.1336 + bool aConsiderSpans, 1.1337 + int32_t aRgFirstRowIndex, 1.1338 + nsIntRect& aDamageArea) 1.1339 +{ 1.1340 + int32_t numCols = aMap.GetColCount(); 1.1341 + NS_ASSERTION(aFirstRowIndex >= 0, "nsCellMap::InsertRows called with negative rowIndex"); 1.1342 + if (uint32_t(aFirstRowIndex) > mRows.Length()) { 1.1343 + // create (aFirstRowIndex - mRows.Length()) empty rows up to aFirstRowIndex 1.1344 + int32_t numEmptyRows = aFirstRowIndex - mRows.Length(); 1.1345 + if (!Grow(aMap, numEmptyRows)) { 1.1346 + return; 1.1347 + } 1.1348 + } 1.1349 + 1.1350 + if (!aConsiderSpans) { 1.1351 + // update mContentRowCount, since non-empty rows will be added 1.1352 + mContentRowCount = std::max(aFirstRowIndex, mContentRowCount); 1.1353 + ExpandWithRows(aMap, aRows, aFirstRowIndex, aRgFirstRowIndex, aDamageArea); 1.1354 + return; 1.1355 + } 1.1356 + 1.1357 + // if any cells span into or out of the row being inserted, then rebuild 1.1358 + bool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex, 1.1359 + aFirstRowIndex, 0, numCols - 1); 1.1360 + 1.1361 + // update mContentRowCount, since non-empty rows will be added 1.1362 + mContentRowCount = std::max(aFirstRowIndex, mContentRowCount); 1.1363 + 1.1364 + // if any of the new cells span out of the new rows being added, then rebuild 1.1365 + // XXX it would be better to only rebuild the portion of the map that follows the new rows 1.1366 + if (!spansCauseRebuild && (uint32_t(aFirstRowIndex) < mRows.Length())) { 1.1367 + spansCauseRebuild = CellsSpanOut(aRows); 1.1368 + } 1.1369 + if (spansCauseRebuild) { 1.1370 + aMap.RebuildConsideringRows(this, aFirstRowIndex, &aRows, 0, aDamageArea); 1.1371 + } 1.1372 + else { 1.1373 + ExpandWithRows(aMap, aRows, aFirstRowIndex, aRgFirstRowIndex, aDamageArea); 1.1374 + } 1.1375 +} 1.1376 + 1.1377 +void 1.1378 +nsCellMap::RemoveRows(nsTableCellMap& aMap, 1.1379 + int32_t aFirstRowIndex, 1.1380 + int32_t aNumRowsToRemove, 1.1381 + bool aConsiderSpans, 1.1382 + int32_t aRgFirstRowIndex, 1.1383 + nsIntRect& aDamageArea) 1.1384 +{ 1.1385 + int32_t numRows = mRows.Length(); 1.1386 + int32_t numCols = aMap.GetColCount(); 1.1387 + 1.1388 + if (aFirstRowIndex >= numRows) { 1.1389 + // reduce the content based row count based on the function arguments 1.1390 + // as they are known to be real rows even if the cell map did not create 1.1391 + // rows for them before. 1.1392 + mContentRowCount -= aNumRowsToRemove; 1.1393 + return; 1.1394 + } 1.1395 + if (!aConsiderSpans) { 1.1396 + ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aRgFirstRowIndex, 1.1397 + aDamageArea); 1.1398 + return; 1.1399 + } 1.1400 + int32_t endRowIndex = aFirstRowIndex + aNumRowsToRemove - 1; 1.1401 + if (endRowIndex >= numRows) { 1.1402 + NS_ERROR("nsCellMap::RemoveRows tried to remove too many rows"); 1.1403 + endRowIndex = numRows - 1; 1.1404 + } 1.1405 + bool spansCauseRebuild = CellsSpanInOrOut(aFirstRowIndex, endRowIndex, 1.1406 + 0, numCols - 1); 1.1407 + if (spansCauseRebuild) { 1.1408 + aMap.RebuildConsideringRows(this, aFirstRowIndex, nullptr, aNumRowsToRemove, 1.1409 + aDamageArea); 1.1410 + } 1.1411 + else { 1.1412 + ShrinkWithoutRows(aMap, aFirstRowIndex, aNumRowsToRemove, aRgFirstRowIndex, 1.1413 + aDamageArea); 1.1414 + } 1.1415 +} 1.1416 + 1.1417 + 1.1418 + 1.1419 + 1.1420 +CellData* 1.1421 +nsCellMap::AppendCell(nsTableCellMap& aMap, 1.1422 + nsTableCellFrame* aCellFrame, 1.1423 + int32_t aRowIndex, 1.1424 + bool aRebuildIfNecessary, 1.1425 + int32_t aRgFirstRowIndex, 1.1426 + nsIntRect& aDamageArea, 1.1427 + int32_t* aColToBeginSearch) 1.1428 +{ 1.1429 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.1430 + int32_t origNumMapRows = mRows.Length(); 1.1431 + int32_t origNumCols = aMap.GetColCount(); 1.1432 + bool zeroRowSpan = false; 1.1433 + int32_t rowSpan = (aCellFrame) ? GetRowSpanForNewCell(aCellFrame, aRowIndex, 1.1434 + zeroRowSpan) : 1; 1.1435 + // add new rows if necessary 1.1436 + int32_t endRowIndex = aRowIndex + rowSpan - 1; 1.1437 + if (endRowIndex >= origNumMapRows) { 1.1438 + // XXXbz handle allocation failures? 1.1439 + Grow(aMap, 1 + endRowIndex - origNumMapRows); 1.1440 + } 1.1441 + 1.1442 + // get the first null or dead CellData in the desired row. It will equal origNumCols if there are none 1.1443 + CellData* origData = nullptr; 1.1444 + int32_t startColIndex = 0; 1.1445 + if (aColToBeginSearch) 1.1446 + startColIndex = *aColToBeginSearch; 1.1447 + for (; startColIndex < origNumCols; startColIndex++) { 1.1448 + CellData* data = GetDataAt(aRowIndex, startColIndex); 1.1449 + if (!data) 1.1450 + break; 1.1451 + // The border collapse code relies on having multiple dead cell data entries 1.1452 + // in a row. 1.1453 + if (data->IsDead() && aCellFrame) { 1.1454 + origData = data; 1.1455 + break; 1.1456 + } 1.1457 + if (data->IsZeroColSpan() ) { 1.1458 + // appending a cell collapses zerospans. 1.1459 + CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex); 1.1460 + // ask again for the data as it should be modified 1.1461 + origData = GetDataAt(aRowIndex, startColIndex); 1.1462 + NS_ASSERTION(origData->IsDead(), 1.1463 + "The cellposition should have been cleared"); 1.1464 + break; 1.1465 + } 1.1466 + } 1.1467 + // We found the place to append the cell, when the next cell is appended 1.1468 + // the next search does not need to duplicate the search but can start 1.1469 + // just at the next cell. 1.1470 + if (aColToBeginSearch) 1.1471 + *aColToBeginSearch = startColIndex + 1; 1.1472 + 1.1473 + bool zeroColSpan = false; 1.1474 + int32_t colSpan = (aCellFrame) ? 1.1475 + GetColSpanForNewCell(*aCellFrame, zeroColSpan) : 1; 1.1476 + if (zeroColSpan) { 1.1477 + aMap.mTableFrame.SetHasZeroColSpans(true); 1.1478 + aMap.mTableFrame.SetNeedColSpanExpansion(true); 1.1479 + } 1.1480 + 1.1481 + // if the new cell could potentially span into other rows and collide with 1.1482 + // originating cells there, we will play it safe and just rebuild the map 1.1483 + if (aRebuildIfNecessary && (aRowIndex < mContentRowCount - 1) && (rowSpan > 1)) { 1.1484 + nsAutoTArray<nsTableCellFrame*, 1> newCellArray; 1.1485 + newCellArray.AppendElement(aCellFrame); 1.1486 + aMap.RebuildConsideringCells(this, &newCellArray, aRowIndex, startColIndex, true, aDamageArea); 1.1487 + return origData; 1.1488 + } 1.1489 + mContentRowCount = std::max(mContentRowCount, aRowIndex + 1); 1.1490 + 1.1491 + // add new cols to the table map if necessary 1.1492 + int32_t endColIndex = startColIndex + colSpan - 1; 1.1493 + if (endColIndex >= origNumCols) { 1.1494 + NS_ASSERTION(aCellFrame, "dead cells should not require new columns"); 1.1495 + aMap.AddColsAtEnd(1 + endColIndex - origNumCols); 1.1496 + } 1.1497 + 1.1498 + // Setup CellData for this cell 1.1499 + if (origData) { 1.1500 + NS_ASSERTION(origData->IsDead(), "replacing a non dead cell is a memory leak"); 1.1501 + if (aCellFrame) { // do nothing to replace a dead cell with a dead cell 1.1502 + origData->Init(aCellFrame); 1.1503 + // we are replacing a dead cell, increase the number of cells 1.1504 + // originating at this column 1.1505 + nsColInfo* colInfo = aMap.GetColInfoAt(startColIndex); 1.1506 + NS_ASSERTION(colInfo, "access to a non existing column"); 1.1507 + if (colInfo) { 1.1508 + colInfo->mNumCellsOrig++; 1.1509 + } 1.1510 + } 1.1511 + } 1.1512 + else { 1.1513 + origData = AllocCellData(aCellFrame); 1.1514 + if (!origData) ABORT1(origData); 1.1515 + SetDataAt(aMap, *origData, aRowIndex, startColIndex); 1.1516 + } 1.1517 + 1.1518 + if (aRebuildIfNecessary) { 1.1519 + //the caller depends on the damageArea 1.1520 + // The special case for zeroRowSpan is to adjust for the '2' in 1.1521 + // GetRowSpanForNewCell. 1.1522 + uint32_t height = zeroRowSpan ? endRowIndex - aRowIndex : 1.1523 + 1 + endRowIndex - aRowIndex; 1.1524 + SetDamageArea(startColIndex, aRgFirstRowIndex + aRowIndex, 1.1525 + 1 + endColIndex - startColIndex, height, aDamageArea); 1.1526 + } 1.1527 + 1.1528 + if (!aCellFrame) { 1.1529 + return origData; 1.1530 + } 1.1531 + 1.1532 + // initialize the cell frame 1.1533 + aCellFrame->SetColIndex(startColIndex); 1.1534 + 1.1535 + // Create CellData objects for the rows that this cell spans. Set 1.1536 + // their mOrigCell to nullptr and their mSpanData to point to data. 1.1537 + for (int32_t rowX = aRowIndex; rowX <= endRowIndex; rowX++) { 1.1538 + // The row at rowX will need to have at least endColIndex columns 1.1539 + mRows[rowX].SetCapacity(endColIndex); 1.1540 + for (int32_t colX = startColIndex; colX <= endColIndex; colX++) { 1.1541 + if ((rowX != aRowIndex) || (colX != startColIndex)) { // skip orig cell data done above 1.1542 + CellData* cellData = GetDataAt(rowX, colX); 1.1543 + if (cellData) { 1.1544 + if (cellData->IsOrig()) { 1.1545 + NS_ERROR("cannot overlap originating cell"); 1.1546 + continue; 1.1547 + } 1.1548 + if (rowX > aRowIndex) { // row spanning into cell 1.1549 + if (cellData->IsRowSpan()) { 1.1550 + // do nothing, this can be caused by rowspan which is overlapped 1.1551 + // by a another cell with a rowspan and a colspan 1.1552 + } 1.1553 + else { 1.1554 + cellData->SetRowSpanOffset(rowX - aRowIndex); 1.1555 + if (zeroRowSpan) { 1.1556 + cellData->SetZeroRowSpan(true); 1.1557 + } 1.1558 + } 1.1559 + } 1.1560 + if (colX > startColIndex) { // col spanning into cell 1.1561 + if (!cellData->IsColSpan()) { 1.1562 + if (cellData->IsRowSpan()) { 1.1563 + cellData->SetOverlap(true); 1.1564 + } 1.1565 + cellData->SetColSpanOffset(colX - startColIndex); 1.1566 + if (zeroColSpan) { 1.1567 + cellData->SetZeroColSpan(true); 1.1568 + } 1.1569 + 1.1570 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.1571 + colInfo->mNumCellsSpan++; 1.1572 + } 1.1573 + } 1.1574 + } 1.1575 + else { 1.1576 + cellData = AllocCellData(nullptr); 1.1577 + if (!cellData) return origData; 1.1578 + if (rowX > aRowIndex) { 1.1579 + cellData->SetRowSpanOffset(rowX - aRowIndex); 1.1580 + if (zeroRowSpan) { 1.1581 + cellData->SetZeroRowSpan(true); 1.1582 + } 1.1583 + } 1.1584 + if (colX > startColIndex) { 1.1585 + cellData->SetColSpanOffset(colX - startColIndex); 1.1586 + if (zeroColSpan) { 1.1587 + cellData->SetZeroColSpan(true); 1.1588 + } 1.1589 + } 1.1590 + SetDataAt(aMap, *cellData, rowX, colX); 1.1591 + } 1.1592 + } 1.1593 + } 1.1594 + } 1.1595 +#ifdef DEBUG_TABLE_CELLMAP 1.1596 + printf("appended cell=%p row=%d \n", aCellFrame, aRowIndex); 1.1597 + aMap.Dump(); 1.1598 +#endif 1.1599 + return origData; 1.1600 +} 1.1601 + 1.1602 +void nsCellMap::CollapseZeroColSpan(nsTableCellMap& aMap, 1.1603 + CellData* aOrigData, 1.1604 + int32_t aRowIndex, 1.1605 + int32_t aColIndex) 1.1606 +{ 1.1607 + // if after a colspan = 0 cell another cell is appended in a row the html 4 1.1608 + // spec is already violated. In principle one should then append the cell 1.1609 + // after the last column but then the zero spanning cell would also have 1.1610 + // to grow. The only plausible way to break this cycle is ignore the zero 1.1611 + // colspan and reset the cell to colspan = 1. 1.1612 + 1.1613 + NS_ASSERTION(aOrigData && aOrigData->IsZeroColSpan(), 1.1614 + "zero colspan should have been passed"); 1.1615 + // find the originating cellframe 1.1616 + nsTableCellFrame* cell = GetCellFrame(aRowIndex, aColIndex, *aOrigData, true); 1.1617 + NS_ASSERTION(cell, "originating cell not found"); 1.1618 + 1.1619 + // find the clearing region 1.1620 + int32_t startRowIndex = aRowIndex - aOrigData->GetRowSpanOffset(); 1.1621 + bool zeroSpan; 1.1622 + int32_t rowSpan = GetRowSpanForNewCell(cell, startRowIndex, zeroSpan); 1.1623 + int32_t endRowIndex = startRowIndex + rowSpan; 1.1624 + 1.1625 + int32_t origColIndex = aColIndex - aOrigData->GetColSpanOffset(); 1.1626 + int32_t endColIndex = origColIndex + 1.1627 + GetEffectiveColSpan(aMap, startRowIndex, 1.1628 + origColIndex, zeroSpan); 1.1629 + for (int32_t colX = origColIndex +1; colX < endColIndex; colX++) { 1.1630 + // Start the collapse just after the originating cell, since 1.1631 + // we're basically making the originating cell act as if it 1.1632 + // has colspan="1". 1.1633 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.1634 + colInfo->mNumCellsSpan -= rowSpan; 1.1635 + 1.1636 + for (int32_t rowX = startRowIndex; rowX < endRowIndex; rowX++) 1.1637 + { 1.1638 + CellData* data = mRows[rowX][colX]; 1.1639 + NS_ASSERTION(data->IsZeroColSpan(), 1.1640 + "Overwriting previous data - memory leak"); 1.1641 + data->Init(nullptr); // mark the cell as a dead cell. 1.1642 + } 1.1643 + } 1.1644 +} 1.1645 + 1.1646 +bool nsCellMap::CellsSpanOut(nsTArray<nsTableRowFrame*>& aRows) const 1.1647 +{ 1.1648 + int32_t numNewRows = aRows.Length(); 1.1649 + for (int32_t rowX = 0; rowX < numNewRows; rowX++) { 1.1650 + nsIFrame* rowFrame = (nsIFrame *) aRows.ElementAt(rowX); 1.1651 + nsIFrame* childFrame = rowFrame->GetFirstPrincipalChild(); 1.1652 + while (childFrame) { 1.1653 + nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); 1.1654 + if (cellFrame) { 1.1655 + bool zeroSpan; 1.1656 + int32_t rowSpan = GetRowSpanForNewCell(cellFrame, rowX, zeroSpan); 1.1657 + if (zeroSpan || rowX + rowSpan > numNewRows) { 1.1658 + return true; 1.1659 + } 1.1660 + } 1.1661 + childFrame = childFrame->GetNextSibling(); 1.1662 + } 1.1663 + } 1.1664 + return false; 1.1665 +} 1.1666 + 1.1667 +// return true if any cells have rows spans into or out of the region 1.1668 +// defined by the row and col indices or any cells have colspans into the region 1.1669 +bool nsCellMap::CellsSpanInOrOut(int32_t aStartRowIndex, 1.1670 + int32_t aEndRowIndex, 1.1671 + int32_t aStartColIndex, 1.1672 + int32_t aEndColIndex) const 1.1673 +{ 1.1674 + /* 1.1675 + * this routine will watch the cells adjacent to the region or at the edge 1.1676 + * they are marked with *. The routine will verify whether they span in or 1.1677 + * are spanned out. 1.1678 + * 1.1679 + * startCol endCol 1.1680 + * r1c1 r1c2 r1c3 r1c4 r1c5 r1rc6 r1c7 1.1681 + * startrow r2c1 r2c2 *r2c3 *r2c4 *r2c5 *r2rc6 r2c7 1.1682 + * endrow r3c1 r3c2 *r3c3 r3c4 r3c5 *r3rc6 r3c7 1.1683 + * r4c1 r4c2 *r4c3 *r4c4 *r4c5 r4rc6 r4c7 1.1684 + * r5c1 r5c2 r5c3 r5c4 r5c5 r5rc6 r5c7 1.1685 + */ 1.1686 + 1.1687 + int32_t numRows = mRows.Length(); // use the cellmap rows to determine the 1.1688 + // current cellmap extent. 1.1689 + for (int32_t colX = aStartColIndex; colX <= aEndColIndex; colX++) { 1.1690 + CellData* cellData; 1.1691 + if (aStartRowIndex > 0) { 1.1692 + cellData = GetDataAt(aStartRowIndex, colX); 1.1693 + if (cellData && (cellData->IsRowSpan())) { 1.1694 + return true; // there is a row span into the region 1.1695 + } 1.1696 + if ((aStartRowIndex >= mContentRowCount) && (mContentRowCount > 0)) { 1.1697 + cellData = GetDataAt(mContentRowCount - 1, colX); 1.1698 + if (cellData && cellData->IsZeroRowSpan()) { 1.1699 + return true; // When we expand the zerospan it'll span into our row 1.1700 + } 1.1701 + } 1.1702 + } 1.1703 + if (aEndRowIndex < numRows - 1) { // is there anything below aEndRowIndex 1.1704 + cellData = GetDataAt(aEndRowIndex + 1, colX); 1.1705 + if ((cellData) && (cellData->IsRowSpan())) { 1.1706 + return true; // there is a row span out of the region 1.1707 + } 1.1708 + } 1.1709 + else { 1.1710 + cellData = GetDataAt(aEndRowIndex, colX); 1.1711 + if ((cellData) && (cellData->IsRowSpan()) && (mContentRowCount < numRows)) { 1.1712 + return true; // this cell might be the cause of a dead row 1.1713 + } 1.1714 + } 1.1715 + } 1.1716 + if (aStartColIndex > 0) { 1.1717 + for (int32_t rowX = aStartRowIndex; rowX <= aEndRowIndex; rowX++) { 1.1718 + CellData* cellData = GetDataAt(rowX, aStartColIndex); 1.1719 + if (cellData && (cellData->IsColSpan())) { 1.1720 + return true; // there is a col span into the region 1.1721 + } 1.1722 + cellData = GetDataAt(rowX, aEndColIndex + 1); 1.1723 + if (cellData && (cellData->IsColSpan())) { 1.1724 + return true; // there is a col span out of the region 1.1725 + } 1.1726 + } 1.1727 + } 1.1728 + return false; 1.1729 +} 1.1730 + 1.1731 +void nsCellMap::InsertCells(nsTableCellMap& aMap, 1.1732 + nsTArray<nsTableCellFrame*>& aCellFrames, 1.1733 + int32_t aRowIndex, 1.1734 + int32_t aColIndexBefore, 1.1735 + int32_t aRgFirstRowIndex, 1.1736 + nsIntRect& aDamageArea) 1.1737 +{ 1.1738 + if (aCellFrames.Length() == 0) return; 1.1739 + NS_ASSERTION(aColIndexBefore >= -1, "index out of range"); 1.1740 + int32_t numCols = aMap.GetColCount(); 1.1741 + if (aColIndexBefore >= numCols) { 1.1742 + NS_ERROR("Inserting instead of appending cells indicates a serious cellmap error"); 1.1743 + aColIndexBefore = numCols - 1; 1.1744 + } 1.1745 + 1.1746 + // get the starting col index of the 1st new cells 1.1747 + int32_t startColIndex; 1.1748 + for (startColIndex = aColIndexBefore + 1; startColIndex < numCols; startColIndex++) { 1.1749 + CellData* data = GetDataAt(aRowIndex, startColIndex); 1.1750 + if (!data || data->IsOrig() || data->IsDead()) { 1.1751 + // // Not a span. Stop. 1.1752 + break; 1.1753 + } 1.1754 + if (data->IsZeroColSpan()) { 1.1755 + // Zero colspans collapse. Stop in this case too. 1.1756 + CollapseZeroColSpan(aMap, data, aRowIndex, startColIndex); 1.1757 + break; 1.1758 + } 1.1759 + } 1.1760 + 1.1761 + // record whether inserted cells are going to cause complications due 1.1762 + // to existing row spans, col spans or table sizing. 1.1763 + bool spansCauseRebuild = false; 1.1764 + 1.1765 + // check that all cells have the same row span 1.1766 + int32_t numNewCells = aCellFrames.Length(); 1.1767 + bool zeroRowSpan = false; 1.1768 + int32_t rowSpan = 0; 1.1769 + for (int32_t cellX = 0; cellX < numNewCells; cellX++) { 1.1770 + nsTableCellFrame* cell = aCellFrames.ElementAt(cellX); 1.1771 + int32_t rowSpan2 = GetRowSpanForNewCell(cell, aRowIndex, zeroRowSpan); 1.1772 + if (rowSpan == 0) { 1.1773 + rowSpan = rowSpan2; 1.1774 + } 1.1775 + else if (rowSpan != rowSpan2) { 1.1776 + spansCauseRebuild = true; 1.1777 + break; 1.1778 + } 1.1779 + } 1.1780 + 1.1781 + // check if the new cells will cause the table to add more rows 1.1782 + if (!spansCauseRebuild) { 1.1783 + if (mRows.Length() < uint32_t(aRowIndex + rowSpan)) { 1.1784 + spansCauseRebuild = true; 1.1785 + } 1.1786 + } 1.1787 + 1.1788 + if (!spansCauseRebuild) { 1.1789 + spansCauseRebuild = CellsSpanInOrOut(aRowIndex, aRowIndex + rowSpan - 1, 1.1790 + startColIndex, numCols - 1); 1.1791 + } 1.1792 + if (spansCauseRebuild) { 1.1793 + aMap.RebuildConsideringCells(this, &aCellFrames, aRowIndex, startColIndex, 1.1794 + true, aDamageArea); 1.1795 + } 1.1796 + else { 1.1797 + ExpandWithCells(aMap, aCellFrames, aRowIndex, startColIndex, rowSpan, 1.1798 + zeroRowSpan, aRgFirstRowIndex, aDamageArea); 1.1799 + } 1.1800 +} 1.1801 + 1.1802 +void 1.1803 +nsCellMap::ExpandWithRows(nsTableCellMap& aMap, 1.1804 + nsTArray<nsTableRowFrame*>& aRowFrames, 1.1805 + int32_t aStartRowIndexIn, 1.1806 + int32_t aRgFirstRowIndex, 1.1807 + nsIntRect& aDamageArea) 1.1808 +{ 1.1809 + int32_t startRowIndex = (aStartRowIndexIn >= 0) ? aStartRowIndexIn : 0; 1.1810 + NS_ASSERTION(uint32_t(startRowIndex) <= mRows.Length(), "caller should have grown cellmap before"); 1.1811 + 1.1812 + int32_t numNewRows = aRowFrames.Length(); 1.1813 + mContentRowCount += numNewRows; 1.1814 + 1.1815 + int32_t endRowIndex = startRowIndex + numNewRows - 1; 1.1816 + 1.1817 + // shift the rows after startRowIndex down and insert empty rows that will 1.1818 + // be filled via the AppendCell call below 1.1819 + if (!Grow(aMap, numNewRows, startRowIndex)) { 1.1820 + return; 1.1821 + } 1.1822 + 1.1823 + 1.1824 + int32_t newRowIndex = 0; 1.1825 + for (int32_t rowX = startRowIndex; rowX <= endRowIndex; rowX++) { 1.1826 + nsTableRowFrame* rFrame = aRowFrames.ElementAt(newRowIndex); 1.1827 + // append cells 1.1828 + nsIFrame* cFrame = rFrame->GetFirstPrincipalChild(); 1.1829 + int32_t colIndex = 0; 1.1830 + while (cFrame) { 1.1831 + nsTableCellFrame *cellFrame = do_QueryFrame(cFrame); 1.1832 + if (cellFrame) { 1.1833 + AppendCell(aMap, cellFrame, rowX, false, aRgFirstRowIndex, aDamageArea, 1.1834 + &colIndex); 1.1835 + } 1.1836 + cFrame = cFrame->GetNextSibling(); 1.1837 + } 1.1838 + newRowIndex++; 1.1839 + } 1.1840 + // mark all following rows damaged, they might contain a previously set 1.1841 + // damage area which we can not shift. 1.1842 + int32_t firstDamagedRow = aRgFirstRowIndex + startRowIndex; 1.1843 + SetDamageArea(0, firstDamagedRow, aMap.GetColCount(), 1.1844 + aMap.GetRowCount() - firstDamagedRow, aDamageArea); 1.1845 +} 1.1846 + 1.1847 +void nsCellMap::ExpandWithCells(nsTableCellMap& aMap, 1.1848 + nsTArray<nsTableCellFrame*>& aCellFrames, 1.1849 + int32_t aRowIndex, 1.1850 + int32_t aColIndex, 1.1851 + int32_t aRowSpan, // same for all cells 1.1852 + bool aRowSpanIsZero, 1.1853 + int32_t aRgFirstRowIndex, 1.1854 + nsIntRect& aDamageArea) 1.1855 +{ 1.1856 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.1857 + int32_t endRowIndex = aRowIndex + aRowSpan - 1; 1.1858 + int32_t startColIndex = aColIndex; 1.1859 + int32_t endColIndex = aColIndex; 1.1860 + int32_t numCells = aCellFrames.Length(); 1.1861 + int32_t totalColSpan = 0; 1.1862 + 1.1863 + // add cellData entries for the space taken up by the new cells 1.1864 + for (int32_t cellX = 0; cellX < numCells; cellX++) { 1.1865 + nsTableCellFrame* cellFrame = aCellFrames.ElementAt(cellX); 1.1866 + CellData* origData = AllocCellData(cellFrame); // the originating cell 1.1867 + if (!origData) return; 1.1868 + 1.1869 + // set the starting and ending col index for the new cell 1.1870 + bool zeroColSpan = false; 1.1871 + int32_t colSpan = GetColSpanForNewCell(*cellFrame, zeroColSpan); 1.1872 + if (zeroColSpan) { 1.1873 + aMap.mTableFrame.SetHasZeroColSpans(true); 1.1874 + aMap.mTableFrame.SetNeedColSpanExpansion(true); 1.1875 + } 1.1876 + totalColSpan += colSpan; 1.1877 + if (cellX == 0) { 1.1878 + endColIndex = aColIndex + colSpan - 1; 1.1879 + } 1.1880 + else { 1.1881 + startColIndex = endColIndex + 1; 1.1882 + endColIndex = startColIndex + colSpan - 1; 1.1883 + } 1.1884 + 1.1885 + // add the originating cell data and any cell data corresponding to row/col spans 1.1886 + for (int32_t rowX = aRowIndex; rowX <= endRowIndex; rowX++) { 1.1887 + CellDataArray& row = mRows[rowX]; 1.1888 + // Pre-allocate all the cells we'll need in this array, setting 1.1889 + // them to null. 1.1890 + // Have to have the cast to get the template to do the right thing. 1.1891 + int32_t insertionIndex = row.Length(); 1.1892 + if (insertionIndex > startColIndex) { 1.1893 + insertionIndex = startColIndex; 1.1894 + } 1.1895 + if (!row.InsertElementsAt(insertionIndex, endColIndex - insertionIndex + 1, 1.1896 + (CellData*)nullptr) && 1.1897 + rowX == aRowIndex) { 1.1898 + // Failed to insert the slots, and this is the very first row. That 1.1899 + // means that we need to clean up |origData| before returning, since 1.1900 + // the cellmap doesn't own it yet. 1.1901 + DestroyCellData(origData); 1.1902 + return; 1.1903 + } 1.1904 + 1.1905 + for (int32_t colX = startColIndex; colX <= endColIndex; colX++) { 1.1906 + CellData* data = origData; 1.1907 + if ((rowX != aRowIndex) || (colX != startColIndex)) { 1.1908 + data = AllocCellData(nullptr); 1.1909 + if (!data) return; 1.1910 + if (rowX > aRowIndex) { 1.1911 + data->SetRowSpanOffset(rowX - aRowIndex); 1.1912 + if (aRowSpanIsZero) { 1.1913 + data->SetZeroRowSpan(true); 1.1914 + } 1.1915 + } 1.1916 + if (colX > startColIndex) { 1.1917 + data->SetColSpanOffset(colX - startColIndex); 1.1918 + if (zeroColSpan) { 1.1919 + data->SetZeroColSpan(true); 1.1920 + } 1.1921 + } 1.1922 + } 1.1923 + SetDataAt(aMap, *data, rowX, colX); 1.1924 + } 1.1925 + } 1.1926 + cellFrame->SetColIndex(startColIndex); 1.1927 + } 1.1928 + int32_t damageHeight = std::min(GetRowGroup()->GetRowCount() - aRowIndex, 1.1929 + aRowSpan); 1.1930 + SetDamageArea(aColIndex, aRgFirstRowIndex + aRowIndex, 1.1931 + 1 + endColIndex - aColIndex, damageHeight, aDamageArea); 1.1932 + 1.1933 + int32_t rowX; 1.1934 + 1.1935 + // update the row and col info due to shifting 1.1936 + for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) { 1.1937 + CellDataArray& row = mRows[rowX]; 1.1938 + uint32_t numCols = row.Length(); 1.1939 + uint32_t colX; 1.1940 + for (colX = aColIndex + totalColSpan; colX < numCols; colX++) { 1.1941 + CellData* data = row[colX]; 1.1942 + if (data) { 1.1943 + // increase the origin and span counts beyond the spanned cols 1.1944 + if (data->IsOrig()) { 1.1945 + // a cell that gets moved needs adjustment as well as it new orignating col 1.1946 + data->GetCellFrame()->SetColIndex(colX); 1.1947 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.1948 + colInfo->mNumCellsOrig++; 1.1949 + } 1.1950 + if (data->IsColSpan()) { 1.1951 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.1952 + colInfo->mNumCellsSpan++; 1.1953 + } 1.1954 + 1.1955 + // decrease the origin and span counts within the spanned cols 1.1956 + int32_t colX2 = colX - totalColSpan; 1.1957 + nsColInfo* colInfo2 = aMap.GetColInfoAt(colX2); 1.1958 + if (data->IsOrig()) { 1.1959 + // the old originating col of a moved cell needs adjustment 1.1960 + colInfo2->mNumCellsOrig--; 1.1961 + } 1.1962 + if (data->IsColSpan()) { 1.1963 + colInfo2->mNumCellsSpan--; 1.1964 + } 1.1965 + } 1.1966 + } 1.1967 + } 1.1968 +} 1.1969 + 1.1970 +void nsCellMap::ShrinkWithoutRows(nsTableCellMap& aMap, 1.1971 + int32_t aStartRowIndex, 1.1972 + int32_t aNumRowsToRemove, 1.1973 + int32_t aRgFirstRowIndex, 1.1974 + nsIntRect& aDamageArea) 1.1975 +{ 1.1976 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.1977 + int32_t endRowIndex = aStartRowIndex + aNumRowsToRemove - 1; 1.1978 + uint32_t colCount = aMap.GetColCount(); 1.1979 + for (int32_t rowX = endRowIndex; rowX >= aStartRowIndex; --rowX) { 1.1980 + CellDataArray& row = mRows[rowX]; 1.1981 + uint32_t colX; 1.1982 + for (colX = 0; colX < colCount; colX++) { 1.1983 + CellData* data = row.SafeElementAt(colX); 1.1984 + if (data) { 1.1985 + // Adjust the column counts. 1.1986 + if (data->IsOrig()) { 1.1987 + // Decrement the column count. 1.1988 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.1989 + colInfo->mNumCellsOrig--; 1.1990 + } 1.1991 + // colspan=0 is only counted as a spanned cell in the 1st col it spans 1.1992 + else if (data->IsColSpan()) { 1.1993 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.1994 + colInfo->mNumCellsSpan--; 1.1995 + } 1.1996 + } 1.1997 + } 1.1998 + 1.1999 + uint32_t rowLength = row.Length(); 1.2000 + // Delete our row information. 1.2001 + for (colX = 0; colX < rowLength; colX++) { 1.2002 + DestroyCellData(row[colX]); 1.2003 + } 1.2004 + 1.2005 + mRows.RemoveElementAt(rowX); 1.2006 + 1.2007 + // Decrement our row and next available index counts. 1.2008 + mContentRowCount--; 1.2009 + } 1.2010 + aMap.RemoveColsAtEnd(); 1.2011 + // mark all following rows damaged, they might contain a previously set 1.2012 + // damage area which we can not shift. 1.2013 + int32_t firstDamagedRow = aRgFirstRowIndex + aStartRowIndex; 1.2014 + SetDamageArea(0, firstDamagedRow, aMap.GetColCount(), 1.2015 + aMap.GetRowCount() - firstDamagedRow, aDamageArea); 1.2016 +} 1.2017 + 1.2018 +int32_t nsCellMap::GetColSpanForNewCell(nsTableCellFrame& aCellFrameToAdd, 1.2019 + bool& aIsZeroColSpan) const 1.2020 +{ 1.2021 + aIsZeroColSpan = false; 1.2022 + int32_t colSpan = aCellFrameToAdd.GetColSpan(); 1.2023 + if (0 == colSpan) { 1.2024 + colSpan = 1; // set the min colspan it will be expanded later 1.2025 + aIsZeroColSpan = true; 1.2026 + } 1.2027 + return colSpan; 1.2028 +} 1.2029 + 1.2030 +int32_t nsCellMap::GetEffectiveColSpan(const nsTableCellMap& aMap, 1.2031 + int32_t aRowIndex, 1.2032 + int32_t aColIndex, 1.2033 + bool& aZeroColSpan) const 1.2034 +{ 1.2035 + int32_t numColsInTable = aMap.GetColCount(); 1.2036 + aZeroColSpan = false; 1.2037 + int32_t colSpan = 1; 1.2038 + if (uint32_t(aRowIndex) >= mRows.Length()) { 1.2039 + return colSpan; 1.2040 + } 1.2041 + 1.2042 + const CellDataArray& row = mRows[aRowIndex]; 1.2043 + int32_t colX; 1.2044 + CellData* data; 1.2045 + int32_t maxCols = numColsInTable; 1.2046 + bool hitOverlap = false; // XXX this is not ever being set to true 1.2047 + for (colX = aColIndex + 1; colX < maxCols; colX++) { 1.2048 + data = row.SafeElementAt(colX); 1.2049 + if (data) { 1.2050 + // for an overlapping situation get the colspan from the originating cell and 1.2051 + // use that as the max number of cols to iterate. Since this is rare, only 1.2052 + // pay the price of looking up the cell's colspan here. 1.2053 + if (!hitOverlap && data->IsOverlap()) { 1.2054 + CellData* origData = row.SafeElementAt(aColIndex); 1.2055 + if (origData && origData->IsOrig()) { 1.2056 + nsTableCellFrame* cellFrame = origData->GetCellFrame(); 1.2057 + if (cellFrame) { 1.2058 + // possible change the number of colums to iterate 1.2059 + maxCols = std::min(aColIndex + cellFrame->GetColSpan(), maxCols); 1.2060 + if (colX >= maxCols) 1.2061 + break; 1.2062 + } 1.2063 + } 1.2064 + } 1.2065 + if (data->IsColSpan()) { 1.2066 + colSpan++; 1.2067 + if (data->IsZeroColSpan()) { 1.2068 + aZeroColSpan = true; 1.2069 + } 1.2070 + } 1.2071 + else { 1.2072 + break; 1.2073 + } 1.2074 + } 1.2075 + else break; 1.2076 + } 1.2077 + return colSpan; 1.2078 +} 1.2079 + 1.2080 +int32_t 1.2081 +nsCellMap::GetRowSpanForNewCell(nsTableCellFrame* aCellFrameToAdd, 1.2082 + int32_t aRowIndex, 1.2083 + bool& aIsZeroRowSpan) const 1.2084 +{ 1.2085 + aIsZeroRowSpan = false; 1.2086 + int32_t rowSpan = aCellFrameToAdd->GetRowSpan(); 1.2087 + if (0 == rowSpan) { 1.2088 + // Use a min value of 2 for a zero rowspan to make computations easier 1.2089 + // elsewhere. Zero rowspans are only content dependent! 1.2090 + rowSpan = std::max(2, mContentRowCount - aRowIndex); 1.2091 + aIsZeroRowSpan = true; 1.2092 + } 1.2093 + return rowSpan; 1.2094 +} 1.2095 + 1.2096 +bool nsCellMap::HasMoreThanOneCell(int32_t aRowIndex) const 1.2097 +{ 1.2098 + const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow); 1.2099 + uint32_t maxColIndex = row.Length(); 1.2100 + uint32_t count = 0; 1.2101 + uint32_t colIndex; 1.2102 + for (colIndex = 0; colIndex < maxColIndex; colIndex++) { 1.2103 + CellData* cellData = row[colIndex]; 1.2104 + if (cellData && (cellData->GetCellFrame() || cellData->IsRowSpan())) 1.2105 + count++; 1.2106 + if (count > 1) 1.2107 + return true; 1.2108 + } 1.2109 + return false; 1.2110 +} 1.2111 + 1.2112 +int32_t 1.2113 +nsCellMap::GetNumCellsOriginatingInRow(int32_t aRowIndex) const 1.2114 +{ 1.2115 + const CellDataArray& row = mRows.SafeElementAt(aRowIndex, *sEmptyRow); 1.2116 + uint32_t count = 0; 1.2117 + uint32_t maxColIndex = row.Length(); 1.2118 + uint32_t colIndex; 1.2119 + for (colIndex = 0; colIndex < maxColIndex; colIndex++) { 1.2120 + CellData* cellData = row[colIndex]; 1.2121 + if (cellData && cellData->IsOrig()) 1.2122 + count++; 1.2123 + } 1.2124 + return count; 1.2125 +} 1.2126 + 1.2127 +int32_t nsCellMap::GetRowSpan(int32_t aRowIndex, 1.2128 + int32_t aColIndex, 1.2129 + bool aGetEffective) const 1.2130 +{ 1.2131 + int32_t rowSpan = 1; 1.2132 + int32_t rowCount = (aGetEffective) ? mContentRowCount : mRows.Length(); 1.2133 + int32_t rowX; 1.2134 + for (rowX = aRowIndex + 1; rowX < rowCount; rowX++) { 1.2135 + CellData* data = GetDataAt(rowX, aColIndex); 1.2136 + if (data) { 1.2137 + if (data->IsRowSpan()) { 1.2138 + rowSpan++; 1.2139 + } 1.2140 + else { 1.2141 + break; 1.2142 + } 1.2143 + } 1.2144 + else break; 1.2145 + } 1.2146 + return rowSpan; 1.2147 +} 1.2148 + 1.2149 +void nsCellMap::ShrinkWithoutCell(nsTableCellMap& aMap, 1.2150 + nsTableCellFrame& aCellFrame, 1.2151 + int32_t aRowIndex, 1.2152 + int32_t aColIndex, 1.2153 + int32_t aRgFirstRowIndex, 1.2154 + nsIntRect& aDamageArea) 1.2155 +{ 1.2156 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.2157 + uint32_t colX, rowX; 1.2158 + 1.2159 + // get the rowspan and colspan from the cell map since the content may have changed 1.2160 + bool zeroColSpan; 1.2161 + uint32_t numCols = aMap.GetColCount(); 1.2162 + int32_t rowSpan = GetRowSpan(aRowIndex, aColIndex, true); 1.2163 + uint32_t colSpan = GetEffectiveColSpan(aMap, aRowIndex, aColIndex, zeroColSpan); 1.2164 + uint32_t endRowIndex = aRowIndex + rowSpan - 1; 1.2165 + uint32_t endColIndex = aColIndex + colSpan - 1; 1.2166 + 1.2167 + if (aMap.mTableFrame.HasZeroColSpans()) { 1.2168 + aMap.mTableFrame.SetNeedColSpanExpansion(true); 1.2169 + } 1.2170 + 1.2171 + // adjust the col counts due to the deleted cell before removing it 1.2172 + for (colX = aColIndex; colX <= endColIndex; colX++) { 1.2173 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.2174 + if (colX == uint32_t(aColIndex)) { 1.2175 + colInfo->mNumCellsOrig--; 1.2176 + } 1.2177 + else { 1.2178 + colInfo->mNumCellsSpan--; 1.2179 + } 1.2180 + } 1.2181 + 1.2182 + // remove the deleted cell and cellData entries for it 1.2183 + for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) { 1.2184 + CellDataArray& row = mRows[rowX]; 1.2185 + 1.2186 + // endIndexForRow points at the first slot we don't want to clean up. This 1.2187 + // makes the aColIndex == 0 case work right with our unsigned int colX. 1.2188 + NS_ASSERTION(endColIndex + 1 <= row.Length(), "span beyond the row size!"); 1.2189 + uint32_t endIndexForRow = std::min(endColIndex + 1, row.Length()); 1.2190 + 1.2191 + // Since endIndexForRow <= row.Length(), enough to compare aColIndex to it. 1.2192 + if (uint32_t(aColIndex) < endIndexForRow) { 1.2193 + for (colX = endIndexForRow; colX > uint32_t(aColIndex); colX--) { 1.2194 + DestroyCellData(row[colX-1]); 1.2195 + } 1.2196 + row.RemoveElementsAt(aColIndex, endIndexForRow - aColIndex); 1.2197 + } 1.2198 + } 1.2199 + 1.2200 + numCols = aMap.GetColCount(); 1.2201 + 1.2202 + // update the row and col info due to shifting 1.2203 + for (rowX = aRowIndex; rowX <= endRowIndex; rowX++) { 1.2204 + CellDataArray& row = mRows[rowX]; 1.2205 + for (colX = aColIndex; colX < numCols - colSpan; colX++) { 1.2206 + CellData* data = row.SafeElementAt(colX); 1.2207 + if (data) { 1.2208 + if (data->IsOrig()) { 1.2209 + // a cell that gets moved to the left needs adjustment in its new location 1.2210 + data->GetCellFrame()->SetColIndex(colX); 1.2211 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.2212 + colInfo->mNumCellsOrig++; 1.2213 + // a cell that gets moved to the left needs adjustment in its old location 1.2214 + colInfo = aMap.GetColInfoAt(colX + colSpan); 1.2215 + if (colInfo) { 1.2216 + colInfo->mNumCellsOrig--; 1.2217 + } 1.2218 + } 1.2219 + 1.2220 + else if (data->IsColSpan()) { 1.2221 + // a cell that gets moved to the left needs adjustment 1.2222 + // in its new location 1.2223 + nsColInfo* colInfo = aMap.GetColInfoAt(colX); 1.2224 + colInfo->mNumCellsSpan++; 1.2225 + // a cell that gets moved to the left needs adjustment 1.2226 + // in its old location 1.2227 + colInfo = aMap.GetColInfoAt(colX + colSpan); 1.2228 + if (colInfo) { 1.2229 + colInfo->mNumCellsSpan--; 1.2230 + } 1.2231 + } 1.2232 + } 1.2233 + } 1.2234 + } 1.2235 + aMap.RemoveColsAtEnd(); 1.2236 + SetDamageArea(aColIndex, aRgFirstRowIndex + aRowIndex, 1.2237 + std::max(0, aMap.GetColCount() - aColIndex - 1), 1.2238 + 1 + endRowIndex - aRowIndex, aDamageArea); 1.2239 +} 1.2240 + 1.2241 +void 1.2242 +nsCellMap::RebuildConsideringRows(nsTableCellMap& aMap, 1.2243 + int32_t aStartRowIndex, 1.2244 + nsTArray<nsTableRowFrame*>* aRowsToInsert, 1.2245 + int32_t aNumRowsToRemove) 1.2246 +{ 1.2247 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.2248 + // copy the old cell map into a new array 1.2249 + uint32_t numOrigRows = mRows.Length(); 1.2250 + nsTArray<CellDataArray> origRows; 1.2251 + mRows.SwapElements(origRows); 1.2252 + 1.2253 + int32_t rowNumberChange; 1.2254 + if (aRowsToInsert) { 1.2255 + rowNumberChange = aRowsToInsert->Length(); 1.2256 + } else { 1.2257 + rowNumberChange = -aNumRowsToRemove; 1.2258 + } 1.2259 + 1.2260 + // adjust mContentRowCount based on the function arguments as they are known to 1.2261 + // be real rows. 1.2262 + mContentRowCount += rowNumberChange; 1.2263 + NS_ASSERTION(mContentRowCount >= 0, "previous mContentRowCount was wrong"); 1.2264 + // mRows is empty now. Grow it to the size we expect it to have. 1.2265 + if (mContentRowCount) { 1.2266 + if (!Grow(aMap, mContentRowCount)) { 1.2267 + // Bail, I guess... Not sure what else we can do here. 1.2268 + return; 1.2269 + } 1.2270 + } 1.2271 + 1.2272 + // aStartRowIndex might be after all existing rows so we should limit the 1.2273 + // copy to the amount of exisiting rows 1.2274 + uint32_t copyEndRowIndex = std::min(numOrigRows, uint32_t(aStartRowIndex)); 1.2275 + 1.2276 + // rowX keeps track of where we are in mRows while setting up the 1.2277 + // new cellmap. 1.2278 + uint32_t rowX = 0; 1.2279 + nsIntRect damageArea; 1.2280 + // put back the rows before the affected ones just as before. Note that we 1.2281 + // can't just copy the old rows in bit-for-bit, because they might be 1.2282 + // spanning out into the rows we're adding/removing. 1.2283 + for ( ; rowX < copyEndRowIndex; rowX++) { 1.2284 + const CellDataArray& row = origRows[rowX]; 1.2285 + uint32_t numCols = row.Length(); 1.2286 + for (uint32_t colX = 0; colX < numCols; colX++) { 1.2287 + // put in the original cell from the cell map 1.2288 + const CellData* data = row.ElementAt(colX); 1.2289 + if (data && data->IsOrig()) { 1.2290 + AppendCell(aMap, data->GetCellFrame(), rowX, false, 0, damageArea); 1.2291 + } 1.2292 + } 1.2293 + } 1.2294 + 1.2295 + // Now handle the new rows being inserted, if any. 1.2296 + uint32_t copyStartRowIndex; 1.2297 + rowX = aStartRowIndex; 1.2298 + if (aRowsToInsert) { 1.2299 + // add in the new cells and create rows if necessary 1.2300 + int32_t numNewRows = aRowsToInsert->Length(); 1.2301 + for (int32_t newRowX = 0; newRowX < numNewRows; newRowX++) { 1.2302 + nsTableRowFrame* rFrame = aRowsToInsert->ElementAt(newRowX); 1.2303 + nsIFrame* cFrame = rFrame->GetFirstPrincipalChild(); 1.2304 + while (cFrame) { 1.2305 + nsTableCellFrame *cellFrame = do_QueryFrame(cFrame); 1.2306 + if (cellFrame) { 1.2307 + AppendCell(aMap, cellFrame, rowX, false, 0, damageArea); 1.2308 + } 1.2309 + cFrame = cFrame->GetNextSibling(); 1.2310 + } 1.2311 + rowX++; 1.2312 + } 1.2313 + copyStartRowIndex = aStartRowIndex; 1.2314 + } 1.2315 + else { 1.2316 + copyStartRowIndex = aStartRowIndex + aNumRowsToRemove; 1.2317 + } 1.2318 + 1.2319 + // put back the rows after the affected ones just as before. Again, we can't 1.2320 + // just copy the old bits because that would not handle the new rows spanning 1.2321 + // out or our earlier old rows spanning through the damaged area. 1.2322 + for (uint32_t copyRowX = copyStartRowIndex; copyRowX < numOrigRows; 1.2323 + copyRowX++) { 1.2324 + const CellDataArray& row = origRows[copyRowX]; 1.2325 + uint32_t numCols = row.Length(); 1.2326 + for (uint32_t colX = 0; colX < numCols; colX++) { 1.2327 + // put in the original cell from the cell map 1.2328 + CellData* data = row.ElementAt(colX); 1.2329 + if (data && data->IsOrig()) { 1.2330 + AppendCell(aMap, data->GetCellFrame(), rowX, false, 0, damageArea); 1.2331 + } 1.2332 + } 1.2333 + rowX++; 1.2334 + } 1.2335 + 1.2336 + // delete the old cell map. Now rowX no longer has anything to do with mRows 1.2337 + for (rowX = 0; rowX < numOrigRows; rowX++) { 1.2338 + CellDataArray& row = origRows[rowX]; 1.2339 + uint32_t len = row.Length(); 1.2340 + for (uint32_t colX = 0; colX < len; colX++) { 1.2341 + DestroyCellData(row[colX]); 1.2342 + } 1.2343 + } 1.2344 +} 1.2345 + 1.2346 +void 1.2347 +nsCellMap::RebuildConsideringCells(nsTableCellMap& aMap, 1.2348 + int32_t aNumOrigCols, 1.2349 + nsTArray<nsTableCellFrame*>* aCellFrames, 1.2350 + int32_t aRowIndex, 1.2351 + int32_t aColIndex, 1.2352 + bool aInsert) 1.2353 +{ 1.2354 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.2355 + // copy the old cell map into a new array 1.2356 + int32_t numOrigRows = mRows.Length(); 1.2357 + nsTArray<CellDataArray> origRows; 1.2358 + mRows.SwapElements(origRows); 1.2359 + 1.2360 + int32_t numNewCells = (aCellFrames) ? aCellFrames->Length() : 0; 1.2361 + 1.2362 + // the new cells might extend the previous column number 1.2363 + NS_ASSERTION(aNumOrigCols >= aColIndex, "Appending cells far beyond cellmap data?!"); 1.2364 + int32_t numCols = aInsert ? std::max(aNumOrigCols, aColIndex + 1) : aNumOrigCols; 1.2365 + 1.2366 + // build the new cell map. Hard to say what, if anything, we can preallocate 1.2367 + // here... Should come back to that sometime, perhaps. 1.2368 + int32_t rowX; 1.2369 + nsIntRect damageArea; 1.2370 + for (rowX = 0; rowX < numOrigRows; rowX++) { 1.2371 + const CellDataArray& row = origRows[rowX]; 1.2372 + for (int32_t colX = 0; colX < numCols; colX++) { 1.2373 + if ((rowX == aRowIndex) && (colX == aColIndex)) { 1.2374 + if (aInsert) { // put in the new cells 1.2375 + for (int32_t cellX = 0; cellX < numNewCells; cellX++) { 1.2376 + nsTableCellFrame* cell = aCellFrames->ElementAt(cellX); 1.2377 + if (cell) { 1.2378 + AppendCell(aMap, cell, rowX, false, 0, damageArea); 1.2379 + } 1.2380 + } 1.2381 + } 1.2382 + else { 1.2383 + continue; // do not put the deleted cell back 1.2384 + } 1.2385 + } 1.2386 + // put in the original cell from the cell map 1.2387 + CellData* data = row.SafeElementAt(colX); 1.2388 + if (data && data->IsOrig()) { 1.2389 + AppendCell(aMap, data->GetCellFrame(), rowX, false, 0, damageArea); 1.2390 + } 1.2391 + } 1.2392 + } 1.2393 + if (aInsert && numOrigRows <= aRowIndex) { // append the new cells below the last original row 1.2394 + NS_ASSERTION (numOrigRows == aRowIndex, "Appending cells far beyond the last row"); 1.2395 + for (int32_t cellX = 0; cellX < numNewCells; cellX++) { 1.2396 + nsTableCellFrame* cell = aCellFrames->ElementAt(cellX); 1.2397 + if (cell) { 1.2398 + AppendCell(aMap, cell, aRowIndex, false, 0, damageArea); 1.2399 + } 1.2400 + } 1.2401 + } 1.2402 + 1.2403 + // delete the old cell map 1.2404 + for (rowX = 0; rowX < numOrigRows; rowX++) { 1.2405 + CellDataArray& row = origRows[rowX]; 1.2406 + uint32_t len = row.Length(); 1.2407 + for (uint32_t colX = 0; colX < len; colX++) { 1.2408 + DestroyCellData(row.SafeElementAt(colX)); 1.2409 + } 1.2410 + } 1.2411 + // expand the cellmap to cover empty content rows 1.2412 + if (mRows.Length() < uint32_t(mContentRowCount)) { 1.2413 + Grow(aMap, mContentRowCount - mRows.Length()); 1.2414 + } 1.2415 + 1.2416 +} 1.2417 + 1.2418 +void nsCellMap::RemoveCell(nsTableCellMap& aMap, 1.2419 + nsTableCellFrame* aCellFrame, 1.2420 + int32_t aRowIndex, 1.2421 + int32_t aRgFirstRowIndex, 1.2422 + nsIntRect& aDamageArea) 1.2423 +{ 1.2424 + uint32_t numRows = mRows.Length(); 1.2425 + if (uint32_t(aRowIndex) >= numRows) { 1.2426 + NS_ERROR("bad arg in nsCellMap::RemoveCell"); 1.2427 + return; 1.2428 + } 1.2429 + int32_t numCols = aMap.GetColCount(); 1.2430 + 1.2431 + // Now aRowIndex is guaranteed OK. 1.2432 + 1.2433 + // get the starting col index of the cell to remove 1.2434 + int32_t startColIndex; 1.2435 + for (startColIndex = 0; startColIndex < numCols; startColIndex++) { 1.2436 + CellData* data = mRows[aRowIndex].SafeElementAt(startColIndex); 1.2437 + if (data && (data->IsOrig()) && (aCellFrame == data->GetCellFrame())) { 1.2438 + break; // we found the col index 1.2439 + } 1.2440 + } 1.2441 + 1.2442 + int32_t rowSpan = GetRowSpan(aRowIndex, startColIndex, false); 1.2443 + // record whether removing the cells is going to cause complications due 1.2444 + // to existing row spans, col spans or table sizing. 1.2445 + bool spansCauseRebuild = CellsSpanInOrOut(aRowIndex, 1.2446 + aRowIndex + rowSpan - 1, 1.2447 + startColIndex, numCols - 1); 1.2448 + // XXX if the cell has a col span to the end of the map, and the end has no originating 1.2449 + // cells, we need to assume that this the only such cell, and rebuild so that there are 1.2450 + // no extraneous cols at the end. The same is true for removing rows. 1.2451 + if (!aCellFrame->GetRowSpan() || !aCellFrame->GetColSpan()) 1.2452 + spansCauseRebuild = true; 1.2453 + 1.2454 + if (spansCauseRebuild) { 1.2455 + aMap.RebuildConsideringCells(this, nullptr, aRowIndex, startColIndex, false, 1.2456 + aDamageArea); 1.2457 + } 1.2458 + else { 1.2459 + ShrinkWithoutCell(aMap, *aCellFrame, aRowIndex, startColIndex, 1.2460 + aRgFirstRowIndex, aDamageArea); 1.2461 + } 1.2462 +} 1.2463 + 1.2464 +void nsCellMap::ExpandZeroColSpans(nsTableCellMap& aMap) 1.2465 +{ 1.2466 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.2467 + uint32_t numRows = mRows.Length(); 1.2468 + uint32_t numCols = aMap.GetColCount(); 1.2469 + uint32_t rowIndex, colIndex; 1.2470 + 1.2471 + for (rowIndex = 0; rowIndex < numRows; rowIndex++) { 1.2472 + for (colIndex = 0; colIndex < numCols; colIndex++) { 1.2473 + CellData* data = mRows[rowIndex].SafeElementAt(colIndex); 1.2474 + if (!data || !data->IsOrig()) 1.2475 + continue; 1.2476 + nsTableCellFrame* cell = data->GetCellFrame(); 1.2477 + NS_ASSERTION(cell, "There has to be a cell"); 1.2478 + int32_t cellRowSpan = cell->GetRowSpan(); 1.2479 + int32_t cellColSpan = cell->GetColSpan(); 1.2480 + bool rowZeroSpan = (0 == cell->GetRowSpan()); 1.2481 + bool colZeroSpan = (0 == cell->GetColSpan()); 1.2482 + if (colZeroSpan) { 1.2483 + aMap.mTableFrame.SetHasZeroColSpans(true); 1.2484 + // do the expansion 1.2485 + NS_ASSERTION(numRows > 0, "Bogus numRows"); 1.2486 + NS_ASSERTION(numCols > 0, "Bogus numCols"); 1.2487 + uint32_t endRowIndex = rowZeroSpan ? numRows - 1 : 1.2488 + rowIndex + cellRowSpan - 1; 1.2489 + uint32_t endColIndex = colZeroSpan ? numCols - 1 : 1.2490 + colIndex + cellColSpan - 1; 1.2491 + uint32_t colX, rowX; 1.2492 + colX = colIndex + 1; 1.2493 + while (colX <= endColIndex) { 1.2494 + // look at columns from here to our colspan. For each one, check 1.2495 + // the rows from here to our rowspan to make sure there is no 1.2496 + // obstacle to marking that column as a zerospanned column; if there 1.2497 + // isn't, mark it so 1.2498 + for (rowX = rowIndex; rowX <= endRowIndex; rowX++) { 1.2499 + CellData* oldData = GetDataAt(rowX, colX); 1.2500 + if (oldData) { 1.2501 + if (oldData->IsOrig()) { 1.2502 + break; // something is in the way 1.2503 + } 1.2504 + if (oldData->IsRowSpan()) { 1.2505 + if ((rowX - rowIndex) != oldData->GetRowSpanOffset()) { 1.2506 + break; 1.2507 + } 1.2508 + } 1.2509 + if (oldData->IsColSpan()) { 1.2510 + if ((colX - colIndex) != oldData->GetColSpanOffset()) { 1.2511 + break; 1.2512 + } 1.2513 + } 1.2514 + } 1.2515 + } 1.2516 + if (endRowIndex >= rowX) 1.2517 + break;// we hit something 1.2518 + for (rowX = rowIndex; rowX <= endRowIndex; rowX++) { 1.2519 + CellData* newData = AllocCellData(nullptr); 1.2520 + if (!newData) return; 1.2521 + 1.2522 + newData->SetColSpanOffset(colX - colIndex); 1.2523 + newData->SetZeroColSpan(true); 1.2524 + 1.2525 + if (rowX > rowIndex) { 1.2526 + newData->SetRowSpanOffset(rowX - rowIndex); 1.2527 + if (rowZeroSpan) 1.2528 + newData->SetZeroRowSpan(true); 1.2529 + } 1.2530 + SetDataAt(aMap, *newData, rowX, colX); 1.2531 + } 1.2532 + colX++; 1.2533 + } // while (colX <= endColIndex) 1.2534 + } // if zerocolspan 1.2535 + } 1.2536 + } 1.2537 +} 1.2538 +#ifdef DEBUG 1.2539 +void nsCellMap::Dump(bool aIsBorderCollapse) const 1.2540 +{ 1.2541 + printf("\n ***** START GROUP CELL MAP DUMP ***** %p\n", (void*)this); 1.2542 + nsTableRowGroupFrame* rg = GetRowGroup(); 1.2543 + const nsStyleDisplay* display = rg->StyleDisplay(); 1.2544 + switch (display->mDisplay) { 1.2545 + case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP: 1.2546 + printf(" thead "); 1.2547 + break; 1.2548 + case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP: 1.2549 + printf(" tfoot "); 1.2550 + break; 1.2551 + case NS_STYLE_DISPLAY_TABLE_ROW_GROUP: 1.2552 + printf(" tbody "); 1.2553 + break; 1.2554 + default: 1.2555 + printf("HUH? wrong display type on rowgroup"); 1.2556 + } 1.2557 + uint32_t mapRowCount = mRows.Length(); 1.2558 + printf("mapRowCount=%u tableRowCount=%d\n", mapRowCount, mContentRowCount); 1.2559 + 1.2560 + 1.2561 + uint32_t rowIndex, colIndex; 1.2562 + for (rowIndex = 0; rowIndex < mapRowCount; rowIndex++) { 1.2563 + const CellDataArray& row = mRows[rowIndex]; 1.2564 + printf(" row %d : ", rowIndex); 1.2565 + uint32_t colCount = row.Length(); 1.2566 + for (colIndex = 0; colIndex < colCount; colIndex++) { 1.2567 + CellData* cd = row[colIndex]; 1.2568 + if (cd) { 1.2569 + if (cd->IsOrig()) { 1.2570 + printf("C%d,%d ", rowIndex, colIndex); 1.2571 + } else { 1.2572 + if (cd->IsRowSpan()) { 1.2573 + printf("R "); 1.2574 + } 1.2575 + if (cd->IsColSpan()) { 1.2576 + printf("C "); 1.2577 + } 1.2578 + if (!(cd->IsRowSpan() && cd->IsColSpan())) { 1.2579 + printf(" "); 1.2580 + } 1.2581 + printf(" "); 1.2582 + } 1.2583 + } else { 1.2584 + printf("---- "); 1.2585 + } 1.2586 + } 1.2587 + if (aIsBorderCollapse) { 1.2588 + nscoord size; 1.2589 + BCBorderOwner owner; 1.2590 + mozilla::css::Side side; 1.2591 + bool segStart; 1.2592 + bool bevel; 1.2593 + for (int32_t i = 0; i <= 2; i++) { 1.2594 + printf("\n "); 1.2595 + for (colIndex = 0; colIndex < colCount; colIndex++) { 1.2596 + BCCellData* cd = (BCCellData *)row[colIndex]; 1.2597 + if (cd) { 1.2598 + if (0 == i) { 1.2599 + size = cd->mData.GetTopEdge(owner, segStart); 1.2600 + printf("t=%d%d%d ", int32_t(size), owner, segStart); 1.2601 + } 1.2602 + else if (1 == i) { 1.2603 + size = cd->mData.GetLeftEdge(owner, segStart); 1.2604 + printf("l=%d%d%d ", int32_t(size), owner, segStart); 1.2605 + } 1.2606 + else { 1.2607 + size = cd->mData.GetCorner(side, bevel); 1.2608 + printf("c=%d%d%d ", int32_t(size), side, bevel); 1.2609 + } 1.2610 + } 1.2611 + } 1.2612 + } 1.2613 + } 1.2614 + printf("\n"); 1.2615 + } 1.2616 + 1.2617 + // output info mapping Ci,j to cell address 1.2618 + uint32_t cellCount = 0; 1.2619 + for (uint32_t rIndex = 0; rIndex < mapRowCount; rIndex++) { 1.2620 + const CellDataArray& row = mRows[rIndex]; 1.2621 + uint32_t colCount = row.Length(); 1.2622 + printf(" "); 1.2623 + for (colIndex = 0; colIndex < colCount; colIndex++) { 1.2624 + CellData* cd = row[colIndex]; 1.2625 + if (cd) { 1.2626 + if (cd->IsOrig()) { 1.2627 + nsTableCellFrame* cellFrame = cd->GetCellFrame(); 1.2628 + int32_t cellFrameColIndex; 1.2629 + cellFrame->GetColIndex(cellFrameColIndex); 1.2630 + printf("C%d,%d=%p(%d) ", rIndex, colIndex, (void*)cellFrame, 1.2631 + cellFrameColIndex); 1.2632 + cellCount++; 1.2633 + } 1.2634 + } 1.2635 + } 1.2636 + printf("\n"); 1.2637 + } 1.2638 + 1.2639 + printf(" ***** END GROUP CELL MAP DUMP *****\n"); 1.2640 +} 1.2641 +#endif 1.2642 + 1.2643 +CellData* 1.2644 +nsCellMap::GetDataAt(int32_t aMapRowIndex, 1.2645 + int32_t aColIndex) const 1.2646 +{ 1.2647 + return 1.2648 + mRows.SafeElementAt(aMapRowIndex, *sEmptyRow).SafeElementAt(aColIndex); 1.2649 +} 1.2650 + 1.2651 +// only called if the cell at aMapRowIndex, aColIndex is null or dead 1.2652 +// (the latter from ExpandZeroColSpans). 1.2653 +void nsCellMap::SetDataAt(nsTableCellMap& aMap, 1.2654 + CellData& aNewCell, 1.2655 + int32_t aMapRowIndex, 1.2656 + int32_t aColIndex) 1.2657 +{ 1.2658 + NS_ASSERTION(!!aMap.mBCInfo == mIsBC, "BC state mismatch"); 1.2659 + if (uint32_t(aMapRowIndex) >= mRows.Length()) { 1.2660 + NS_ERROR("SetDataAt called with row index > num rows"); 1.2661 + return; 1.2662 + } 1.2663 + 1.2664 + CellDataArray& row = mRows[aMapRowIndex]; 1.2665 + 1.2666 + // the table map may need cols added 1.2667 + int32_t numColsToAdd = aColIndex + 1 - aMap.GetColCount(); 1.2668 + if (numColsToAdd > 0) { 1.2669 + aMap.AddColsAtEnd(numColsToAdd); 1.2670 + } 1.2671 + // the row may need cols added 1.2672 + numColsToAdd = aColIndex + 1 - row.Length(); 1.2673 + if (numColsToAdd > 0) { 1.2674 + // XXXbz need to handle allocation failures. 1.2675 + GrowRow(row, numColsToAdd); 1.2676 + } 1.2677 + 1.2678 + DestroyCellData(row[aColIndex]); 1.2679 + 1.2680 + row.ReplaceElementsAt(aColIndex, 1, &aNewCell); 1.2681 + // update the originating cell counts if cell originates in this row, col 1.2682 + nsColInfo* colInfo = aMap.GetColInfoAt(aColIndex); 1.2683 + if (colInfo) { 1.2684 + if (aNewCell.IsOrig()) { 1.2685 + colInfo->mNumCellsOrig++; 1.2686 + } 1.2687 + else if (aNewCell.IsColSpan()) { 1.2688 + colInfo->mNumCellsSpan++; 1.2689 + } 1.2690 + } 1.2691 + else NS_ERROR("SetDataAt called with col index > table map num cols"); 1.2692 +} 1.2693 + 1.2694 +nsTableCellFrame* 1.2695 +nsCellMap::GetCellInfoAt(const nsTableCellMap& aMap, 1.2696 + int32_t aRowX, 1.2697 + int32_t aColX, 1.2698 + bool* aOriginates, 1.2699 + int32_t* aColSpan) const 1.2700 +{ 1.2701 + if (aOriginates) { 1.2702 + *aOriginates = false; 1.2703 + } 1.2704 + CellData* data = GetDataAt(aRowX, aColX); 1.2705 + nsTableCellFrame* cellFrame = nullptr; 1.2706 + if (data) { 1.2707 + if (data->IsOrig()) { 1.2708 + cellFrame = data->GetCellFrame(); 1.2709 + if (aOriginates) 1.2710 + *aOriginates = true; 1.2711 + } 1.2712 + else { 1.2713 + cellFrame = GetCellFrame(aRowX, aColX, *data, true); 1.2714 + } 1.2715 + if (cellFrame && aColSpan) { 1.2716 + int32_t initialColIndex; 1.2717 + cellFrame->GetColIndex(initialColIndex); 1.2718 + bool zeroSpan; 1.2719 + *aColSpan = GetEffectiveColSpan(aMap, aRowX, initialColIndex, zeroSpan); 1.2720 + } 1.2721 + } 1.2722 + return cellFrame; 1.2723 +} 1.2724 + 1.2725 + 1.2726 +bool nsCellMap::RowIsSpannedInto(int32_t aRowIndex, 1.2727 + int32_t aNumEffCols) const 1.2728 +{ 1.2729 + if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) { 1.2730 + return false; 1.2731 + } 1.2732 + for (int32_t colIndex = 0; colIndex < aNumEffCols; colIndex++) { 1.2733 + CellData* cd = GetDataAt(aRowIndex, colIndex); 1.2734 + if (cd) { // there's really a cell at (aRowIndex, colIndex) 1.2735 + if (cd->IsSpan()) { // the cell at (aRowIndex, colIndex) is the result of a span 1.2736 + if (cd->IsRowSpan() && GetCellFrame(aRowIndex, colIndex, *cd, true)) { // XXX why the last check 1.2737 + return true; 1.2738 + } 1.2739 + } 1.2740 + } 1.2741 + } 1.2742 + return false; 1.2743 +} 1.2744 + 1.2745 +bool nsCellMap::RowHasSpanningCells(int32_t aRowIndex, 1.2746 + int32_t aNumEffCols) const 1.2747 +{ 1.2748 + if ((0 > aRowIndex) || (aRowIndex >= mContentRowCount)) { 1.2749 + return false; 1.2750 + } 1.2751 + if (aRowIndex != mContentRowCount - 1) { 1.2752 + // aRowIndex is not the last row, so we check the next row after aRowIndex for spanners 1.2753 + for (int32_t colIndex = 0; colIndex < aNumEffCols; colIndex++) { 1.2754 + CellData* cd = GetDataAt(aRowIndex, colIndex); 1.2755 + if (cd && (cd->IsOrig())) { // cell originates 1.2756 + CellData* cd2 = GetDataAt(aRowIndex + 1, colIndex); 1.2757 + if (cd2 && cd2->IsRowSpan()) { // cd2 is spanned by a row 1.2758 + if (cd->GetCellFrame() == GetCellFrame(aRowIndex + 1, colIndex, *cd2, true)) { 1.2759 + return true; 1.2760 + } 1.2761 + } 1.2762 + } 1.2763 + } 1.2764 + } 1.2765 + return false; 1.2766 +} 1.2767 + 1.2768 +void nsCellMap::DestroyCellData(CellData* aData) 1.2769 +{ 1.2770 + if (!aData) { 1.2771 + return; 1.2772 + } 1.2773 + 1.2774 + if (mIsBC) { 1.2775 + BCCellData* bcData = static_cast<BCCellData*>(aData); 1.2776 + bcData->~BCCellData(); 1.2777 + mPresContext->FreeToShell(sizeof(BCCellData), bcData); 1.2778 + } else { 1.2779 + aData->~CellData(); 1.2780 + mPresContext->FreeToShell(sizeof(CellData), aData); 1.2781 + } 1.2782 +} 1.2783 + 1.2784 +CellData* nsCellMap::AllocCellData(nsTableCellFrame* aOrigCell) 1.2785 +{ 1.2786 + if (mIsBC) { 1.2787 + BCCellData* data = (BCCellData*) 1.2788 + mPresContext->AllocateFromShell(sizeof(BCCellData)); 1.2789 + if (data) { 1.2790 + new (data) BCCellData(aOrigCell); 1.2791 + } 1.2792 + return data; 1.2793 + } 1.2794 + 1.2795 + CellData* data = (CellData*) 1.2796 + mPresContext->AllocateFromShell(sizeof(CellData)); 1.2797 + if (data) { 1.2798 + new (data) CellData(aOrigCell); 1.2799 + } 1.2800 + return data; 1.2801 +} 1.2802 + 1.2803 +void 1.2804 +nsCellMapColumnIterator::AdvanceRowGroup() 1.2805 +{ 1.2806 + do { 1.2807 + mCurMapStart += mCurMapContentRowCount; 1.2808 + mCurMap = mCurMap->GetNextSibling(); 1.2809 + if (!mCurMap) { 1.2810 + // Set mCurMapContentRowCount and mCurMapRelevantRowCount to 0 in case 1.2811 + // mCurMap has no next sibling. This can happen if we just handled the 1.2812 + // last originating cell. Future calls will end up with mFoundCells == 1.2813 + // mOrigCells, but for this one mFoundCells was definitely not big enough 1.2814 + // if we got here. 1.2815 + mCurMapContentRowCount = 0; 1.2816 + mCurMapRelevantRowCount = 0; 1.2817 + break; 1.2818 + } 1.2819 + 1.2820 + mCurMapContentRowCount = mCurMap->GetRowCount(); 1.2821 + uint32_t rowArrayLength = mCurMap->mRows.Length(); 1.2822 + mCurMapRelevantRowCount = std::min(mCurMapContentRowCount, rowArrayLength); 1.2823 + } while (0 == mCurMapRelevantRowCount); 1.2824 + 1.2825 + NS_ASSERTION(mCurMapRelevantRowCount != 0 || !mCurMap, 1.2826 + "How did that happen?"); 1.2827 + 1.2828 + // Set mCurMapRow to 0, since cells can't span across table row groups. 1.2829 + mCurMapRow = 0; 1.2830 +} 1.2831 + 1.2832 +void 1.2833 +nsCellMapColumnIterator::IncrementRow(int32_t aIncrement) 1.2834 +{ 1.2835 + NS_PRECONDITION(aIncrement >= 0, "Bogus increment"); 1.2836 + NS_PRECONDITION(mCurMap, "Bogus mOrigCells?"); 1.2837 + if (aIncrement == 0) { 1.2838 + AdvanceRowGroup(); 1.2839 + } 1.2840 + else { 1.2841 + mCurMapRow += aIncrement; 1.2842 + if (mCurMapRow >= mCurMapRelevantRowCount) { 1.2843 + AdvanceRowGroup(); 1.2844 + } 1.2845 + } 1.2846 +} 1.2847 + 1.2848 +nsTableCellFrame* 1.2849 +nsCellMapColumnIterator::GetNextFrame(int32_t* aRow, int32_t* aColSpan) 1.2850 +{ 1.2851 + // Fast-path for the case when we don't have anything left in the column and 1.2852 + // we know it. 1.2853 + if (mFoundCells == mOrigCells) { 1.2854 + *aRow = 0; 1.2855 + *aColSpan = 1; 1.2856 + return nullptr; 1.2857 + } 1.2858 + 1.2859 + while (1) { 1.2860 + NS_ASSERTION(mCurMapRow < mCurMapRelevantRowCount, "Bogus mOrigCells?"); 1.2861 + // Safe to just get the row (which is faster than calling GetDataAt(), but 1.2862 + // there may not be that many cells in it, so have to use SafeElementAt for 1.2863 + // the mCol. 1.2864 + const nsCellMap::CellDataArray& row = mCurMap->mRows[mCurMapRow]; 1.2865 + CellData* cellData = row.SafeElementAt(mCol); 1.2866 + if (!cellData || cellData->IsDead()) { 1.2867 + // Could hit this if there are fewer cells in this row than others, for 1.2868 + // example. 1.2869 + IncrementRow(1); 1.2870 + continue; 1.2871 + } 1.2872 + 1.2873 + if (cellData->IsColSpan()) { 1.2874 + // Look up the originating data for this cell, advance by its relative rowspan. 1.2875 + int32_t rowspanOffset = cellData->GetRowSpanOffset(); 1.2876 + nsTableCellFrame* cellFrame = mCurMap->GetCellFrame(mCurMapRow, mCol, *cellData, false); 1.2877 + NS_ASSERTION(cellFrame,"Must have usable originating data here"); 1.2878 + int32_t rowSpan = cellFrame->GetRowSpan(); 1.2879 + if (rowSpan == 0) { 1.2880 + AdvanceRowGroup(); 1.2881 + } 1.2882 + else { 1.2883 + IncrementRow(rowSpan - rowspanOffset); 1.2884 + } 1.2885 + continue; 1.2886 + } 1.2887 + 1.2888 + NS_ASSERTION(cellData->IsOrig(), 1.2889 + "Must have originating cellData by this point. " 1.2890 + "See comment on mCurMapRow in header."); 1.2891 + 1.2892 + nsTableCellFrame* cellFrame = cellData->GetCellFrame(); 1.2893 + NS_ASSERTION(cellFrame, "Orig data without cellframe?"); 1.2894 + 1.2895 + *aRow = mCurMapStart + mCurMapRow; 1.2896 + bool ignoredZeroSpan; 1.2897 + *aColSpan = mCurMap->GetEffectiveColSpan(*mMap, mCurMapRow, mCol, 1.2898 + ignoredZeroSpan); 1.2899 + 1.2900 + IncrementRow(cellFrame->GetRowSpan()); 1.2901 + 1.2902 + ++mFoundCells; 1.2903 + 1.2904 + NS_ABORT_IF_FALSE(cellData == mMap->GetDataAt(*aRow, mCol), 1.2905 + "Giving caller bogus row?"); 1.2906 + 1.2907 + return cellFrame; 1.2908 + } 1.2909 + 1.2910 + NS_NOTREACHED("Can't get here"); 1.2911 + return nullptr; 1.2912 +}