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