|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=2 sw=2 et tw=80: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "mozilla/Likely.h" |
|
8 #include "mozilla/MathAlgorithms.h" |
|
9 |
|
10 #include "nsCOMPtr.h" |
|
11 #include "nsTableFrame.h" |
|
12 #include "nsRenderingContext.h" |
|
13 #include "nsStyleContext.h" |
|
14 #include "nsStyleConsts.h" |
|
15 #include "nsIContent.h" |
|
16 #include "nsCellMap.h" |
|
17 #include "nsTableCellFrame.h" |
|
18 #include "nsHTMLParts.h" |
|
19 #include "nsTableColFrame.h" |
|
20 #include "nsTableColGroupFrame.h" |
|
21 #include "nsTableRowFrame.h" |
|
22 #include "nsTableRowGroupFrame.h" |
|
23 #include "nsTableOuterFrame.h" |
|
24 #include "nsTablePainter.h" |
|
25 |
|
26 #include "BasicTableLayoutStrategy.h" |
|
27 #include "FixedTableLayoutStrategy.h" |
|
28 |
|
29 #include "nsPresContext.h" |
|
30 #include "nsContentUtils.h" |
|
31 #include "nsCSSRendering.h" |
|
32 #include "nsGkAtoms.h" |
|
33 #include "nsCSSAnonBoxes.h" |
|
34 #include "nsIPresShell.h" |
|
35 #include "nsIDOMElement.h" |
|
36 #include "nsIDOMHTMLElement.h" |
|
37 #include "nsIScriptError.h" |
|
38 #include "nsFrameManager.h" |
|
39 #include "nsError.h" |
|
40 #include "nsAutoPtr.h" |
|
41 #include "nsCSSFrameConstructor.h" |
|
42 #include "nsStyleSet.h" |
|
43 #include "nsDisplayList.h" |
|
44 #include "nsIScrollableFrame.h" |
|
45 #include "nsCSSProps.h" |
|
46 #include "RestyleTracker.h" |
|
47 #include <algorithm> |
|
48 |
|
49 using namespace mozilla; |
|
50 using namespace mozilla::layout; |
|
51 |
|
52 /******************************************************************************** |
|
53 ** nsTableReflowState ** |
|
54 ********************************************************************************/ |
|
55 |
|
56 struct nsTableReflowState { |
|
57 |
|
58 // the real reflow state |
|
59 const nsHTMLReflowState& reflowState; |
|
60 |
|
61 // The table's available size |
|
62 nsSize availSize; |
|
63 |
|
64 // Stationary x-offset |
|
65 nscoord x; |
|
66 |
|
67 // Running y-offset |
|
68 nscoord y; |
|
69 |
|
70 nsTableReflowState(nsPresContext& aPresContext, |
|
71 const nsHTMLReflowState& aReflowState, |
|
72 nsTableFrame& aTableFrame, |
|
73 nscoord aAvailWidth, |
|
74 nscoord aAvailHeight) |
|
75 : reflowState(aReflowState) |
|
76 { |
|
77 Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight); |
|
78 } |
|
79 |
|
80 void Init(nsPresContext& aPresContext, |
|
81 nsTableFrame& aTableFrame, |
|
82 nscoord aAvailWidth, |
|
83 nscoord aAvailHeight) |
|
84 { |
|
85 nsTableFrame* table = static_cast<nsTableFrame*>(aTableFrame.FirstInFlow()); |
|
86 nsMargin borderPadding = table->GetChildAreaOffset(&reflowState); |
|
87 nscoord cellSpacingX = table->GetCellSpacingX(); |
|
88 |
|
89 x = borderPadding.left + cellSpacingX; |
|
90 y = borderPadding.top; //cellspacing added during reflow |
|
91 |
|
92 availSize.width = aAvailWidth; |
|
93 if (NS_UNCONSTRAINEDSIZE != availSize.width) { |
|
94 availSize.width -= borderPadding.left + borderPadding.right |
|
95 + (2 * cellSpacingX); |
|
96 availSize.width = std::max(0, availSize.width); |
|
97 } |
|
98 |
|
99 availSize.height = aAvailHeight; |
|
100 if (NS_UNCONSTRAINEDSIZE != availSize.height) { |
|
101 availSize.height -= borderPadding.top + borderPadding.bottom |
|
102 + (2 * table->GetCellSpacingY()); |
|
103 availSize.height = std::max(0, availSize.height); |
|
104 } |
|
105 } |
|
106 |
|
107 nsTableReflowState(nsPresContext& aPresContext, |
|
108 const nsHTMLReflowState& aReflowState, |
|
109 nsTableFrame& aTableFrame) |
|
110 : reflowState(aReflowState) |
|
111 { |
|
112 Init(aPresContext, aTableFrame, aReflowState.AvailableWidth(), aReflowState.AvailableHeight()); |
|
113 } |
|
114 |
|
115 }; |
|
116 |
|
117 /******************************************************************************** |
|
118 ** nsTableFrame ** |
|
119 ********************************************************************************/ |
|
120 |
|
121 struct BCPropertyData |
|
122 { |
|
123 BCPropertyData() : mTopBorderWidth(0), mRightBorderWidth(0), |
|
124 mBottomBorderWidth(0), mLeftBorderWidth(0), |
|
125 mLeftCellBorderWidth(0), mRightCellBorderWidth(0) {} |
|
126 nsIntRect mDamageArea; |
|
127 BCPixelSize mTopBorderWidth; |
|
128 BCPixelSize mRightBorderWidth; |
|
129 BCPixelSize mBottomBorderWidth; |
|
130 BCPixelSize mLeftBorderWidth; |
|
131 BCPixelSize mLeftCellBorderWidth; |
|
132 BCPixelSize mRightCellBorderWidth; |
|
133 }; |
|
134 |
|
135 nsIFrame* |
|
136 nsTableFrame::GetParentStyleContextFrame() const |
|
137 { |
|
138 // Since our parent, the table outer frame, returned this frame, we |
|
139 // must return whatever our parent would normally have returned. |
|
140 |
|
141 NS_PRECONDITION(mParent, "table constructed without outer table"); |
|
142 if (!mContent->GetParent() && !StyleContext()->GetPseudo()) { |
|
143 // We're the root. We have no style context parent. |
|
144 return nullptr; |
|
145 } |
|
146 |
|
147 return static_cast<nsFrame*>(GetParent())->DoGetParentStyleContextFrame(); |
|
148 } |
|
149 |
|
150 |
|
151 nsIAtom* |
|
152 nsTableFrame::GetType() const |
|
153 { |
|
154 return nsGkAtoms::tableFrame; |
|
155 } |
|
156 |
|
157 |
|
158 nsTableFrame::nsTableFrame(nsStyleContext* aContext) |
|
159 : nsContainerFrame(aContext), |
|
160 mCellMap(nullptr), |
|
161 mTableLayoutStrategy(nullptr) |
|
162 { |
|
163 memset(&mBits, 0, sizeof(mBits)); |
|
164 } |
|
165 |
|
166 void |
|
167 nsTableFrame::Init(nsIContent* aContent, |
|
168 nsIFrame* aParent, |
|
169 nsIFrame* aPrevInFlow) |
|
170 { |
|
171 NS_PRECONDITION(!mCellMap, "Init called twice"); |
|
172 NS_PRECONDITION(!aPrevInFlow || |
|
173 aPrevInFlow->GetType() == nsGkAtoms::tableFrame, |
|
174 "prev-in-flow must be of same type"); |
|
175 |
|
176 // Let the base class do its processing |
|
177 nsContainerFrame::Init(aContent, aParent, aPrevInFlow); |
|
178 |
|
179 // see if border collapse is on, if so set it |
|
180 const nsStyleTableBorder* tableStyle = StyleTableBorder(); |
|
181 bool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse); |
|
182 SetBorderCollapse(borderCollapse); |
|
183 |
|
184 // Create the cell map if this frame is the first-in-flow. |
|
185 if (!aPrevInFlow) { |
|
186 mCellMap = new nsTableCellMap(*this, borderCollapse); |
|
187 } |
|
188 |
|
189 if (aPrevInFlow) { |
|
190 // set my width, because all frames in a table flow are the same width and |
|
191 // code in nsTableOuterFrame depends on this being set |
|
192 mRect.width = aPrevInFlow->GetSize().width; |
|
193 } |
|
194 else { |
|
195 NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called"); |
|
196 // create the strategy |
|
197 if (IsAutoLayout()) |
|
198 mTableLayoutStrategy = new BasicTableLayoutStrategy(this); |
|
199 else |
|
200 mTableLayoutStrategy = new FixedTableLayoutStrategy(this); |
|
201 } |
|
202 } |
|
203 |
|
204 nsTableFrame::~nsTableFrame() |
|
205 { |
|
206 delete mCellMap; |
|
207 delete mTableLayoutStrategy; |
|
208 } |
|
209 |
|
210 void |
|
211 nsTableFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
212 { |
|
213 mColGroups.DestroyFramesFrom(aDestructRoot); |
|
214 nsContainerFrame::DestroyFrom(aDestructRoot); |
|
215 } |
|
216 |
|
217 // Make sure any views are positioned properly |
|
218 void |
|
219 nsTableFrame::RePositionViews(nsIFrame* aFrame) |
|
220 { |
|
221 nsContainerFrame::PositionFrameView(aFrame); |
|
222 nsContainerFrame::PositionChildViews(aFrame); |
|
223 } |
|
224 |
|
225 static bool |
|
226 IsRepeatedFrame(nsIFrame* kidFrame) |
|
227 { |
|
228 return (kidFrame->GetType() == nsGkAtoms::tableRowFrame || |
|
229 kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) && |
|
230 (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP); |
|
231 } |
|
232 |
|
233 bool |
|
234 nsTableFrame::PageBreakAfter(nsIFrame* aSourceFrame, |
|
235 nsIFrame* aNextFrame) |
|
236 { |
|
237 const nsStyleDisplay* display = aSourceFrame->StyleDisplay(); |
|
238 nsTableRowGroupFrame* prevRg = do_QueryFrame(aSourceFrame); |
|
239 // don't allow a page break after a repeated element ... |
|
240 if ((display->mBreakAfter || (prevRg && prevRg->HasInternalBreakAfter())) && |
|
241 !IsRepeatedFrame(aSourceFrame)) { |
|
242 return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before |
|
243 } |
|
244 |
|
245 if (aNextFrame) { |
|
246 display = aNextFrame->StyleDisplay(); |
|
247 // don't allow a page break before a repeated element ... |
|
248 nsTableRowGroupFrame* nextRg = do_QueryFrame(aNextFrame); |
|
249 if ((display->mBreakBefore || |
|
250 (nextRg && nextRg->HasInternalBreakBefore())) && |
|
251 !IsRepeatedFrame(aNextFrame)) { |
|
252 return !IsRepeatedFrame(aSourceFrame); // or after |
|
253 } |
|
254 } |
|
255 return false; |
|
256 } |
|
257 |
|
258 typedef nsTArray<nsIFrame*> FrameTArray; |
|
259 |
|
260 /* static */ void |
|
261 nsTableFrame::DestroyPositionedTablePartArray(void* aPropertyValue) |
|
262 { |
|
263 auto positionedObjs = static_cast<FrameTArray*>(aPropertyValue); |
|
264 delete positionedObjs; |
|
265 } |
|
266 |
|
267 /* static */ void |
|
268 nsTableFrame::RegisterPositionedTablePart(nsIFrame* aFrame) |
|
269 { |
|
270 // Supporting relative positioning for table parts other than table cells has |
|
271 // the potential to break sites that apply 'position: relative' to those |
|
272 // parts, expecting nothing to happen. We warn at the console to make tracking |
|
273 // down the issue easy. |
|
274 if (nsGkAtoms::tableCellFrame != aFrame->GetType()) { |
|
275 nsIContent* content = aFrame->GetContent(); |
|
276 nsPresContext* presContext = aFrame->PresContext(); |
|
277 if (content && !presContext->HasWarnedAboutPositionedTableParts()) { |
|
278 presContext->SetHasWarnedAboutPositionedTableParts(); |
|
279 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, |
|
280 NS_LITERAL_CSTRING("Layout: Tables"), |
|
281 content->OwnerDoc(), |
|
282 nsContentUtils::eLAYOUT_PROPERTIES, |
|
283 "TablePartRelPosWarning"); |
|
284 } |
|
285 } |
|
286 |
|
287 nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(aFrame); |
|
288 MOZ_ASSERT(tableFrame, "Should have a table frame here"); |
|
289 tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation()); |
|
290 |
|
291 // Retrieve the positioned parts array for this table. |
|
292 FrameProperties props = tableFrame->Properties(); |
|
293 auto positionedParts = |
|
294 static_cast<FrameTArray*>(props.Get(PositionedTablePartArray())); |
|
295 |
|
296 // Lazily create the array if it doesn't exist yet. |
|
297 if (!positionedParts) { |
|
298 positionedParts = new FrameTArray; |
|
299 props.Set(PositionedTablePartArray(), positionedParts); |
|
300 } |
|
301 |
|
302 // Add this frame to the list. |
|
303 positionedParts->AppendElement(aFrame); |
|
304 } |
|
305 |
|
306 /* static */ void |
|
307 nsTableFrame::UnregisterPositionedTablePart(nsIFrame* aFrame, |
|
308 nsIFrame* aDestructRoot) |
|
309 { |
|
310 // Retrieve the table frame, and ensure that we hit aDestructRoot on the way. |
|
311 // If we don't, that means that the table frame will be destroyed, so we don't |
|
312 // need to bother with unregistering this frame. |
|
313 nsTableFrame* tableFrame = GetTableFramePassingThrough(aDestructRoot, aFrame); |
|
314 if (!tableFrame) { |
|
315 return; |
|
316 } |
|
317 tableFrame = static_cast<nsTableFrame*>(tableFrame->FirstContinuation()); |
|
318 |
|
319 // Retrieve the positioned parts array for this table. |
|
320 FrameProperties props = tableFrame->Properties(); |
|
321 auto positionedParts = |
|
322 static_cast<FrameTArray*>(props.Get(PositionedTablePartArray())); |
|
323 |
|
324 // Remove the frame. |
|
325 MOZ_ASSERT(positionedParts && |
|
326 positionedParts->IndexOf(aFrame) != FrameTArray::NoIndex, |
|
327 "Asked to unregister a positioned table part that wasn't registered"); |
|
328 if (positionedParts) { |
|
329 positionedParts->RemoveElement(aFrame); |
|
330 } |
|
331 } |
|
332 |
|
333 // XXX this needs to be cleaned up so that the frame constructor breaks out col group |
|
334 // frames into a separate child list, bug 343048. |
|
335 nsresult |
|
336 nsTableFrame::SetInitialChildList(ChildListID aListID, |
|
337 nsFrameList& aChildList) |
|
338 { |
|
339 |
|
340 if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) { |
|
341 // We already have child frames which means we've already been |
|
342 // initialized |
|
343 NS_NOTREACHED("unexpected second call to SetInitialChildList"); |
|
344 return NS_ERROR_UNEXPECTED; |
|
345 } |
|
346 if (aListID != kPrincipalList) { |
|
347 // All we know about is the principal child list. |
|
348 NS_NOTREACHED("unknown frame list"); |
|
349 return NS_ERROR_INVALID_ARG; |
|
350 } |
|
351 |
|
352 // XXXbz the below code is an icky cesspit that's only needed in its current |
|
353 // form for two reasons: |
|
354 // 1) Both rowgroups and column groups come in on the principal child list. |
|
355 while (aChildList.NotEmpty()) { |
|
356 nsIFrame* childFrame = aChildList.FirstChild(); |
|
357 aChildList.RemoveFirstChild(); |
|
358 const nsStyleDisplay* childDisplay = childFrame->StyleDisplay(); |
|
359 |
|
360 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay) { |
|
361 NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(), |
|
362 "This is not a colgroup"); |
|
363 mColGroups.AppendFrame(nullptr, childFrame); |
|
364 } |
|
365 else { // row groups and unknown frames go on the main list for now |
|
366 mFrames.AppendFrame(nullptr, childFrame); |
|
367 } |
|
368 } |
|
369 |
|
370 // If we have a prev-in-flow, then we're a table that has been split and |
|
371 // so don't treat this like an append |
|
372 if (!GetPrevInFlow()) { |
|
373 // process col groups first so that real cols get constructed before |
|
374 // anonymous ones due to cells in rows. |
|
375 InsertColGroups(0, mColGroups); |
|
376 InsertRowGroups(mFrames); |
|
377 // calc collapsing borders |
|
378 if (IsBorderCollapse()) { |
|
379 SetFullBCDamageArea(); |
|
380 } |
|
381 } |
|
382 |
|
383 return NS_OK; |
|
384 } |
|
385 |
|
386 void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame, |
|
387 nsIContent* aContent, |
|
388 nsIAtom* aAttribute) |
|
389 { |
|
390 nsTableCellFrame *cellFrame = do_QueryFrame(aFrame); |
|
391 if (cellFrame) { |
|
392 if ((nsGkAtoms::rowspan == aAttribute) || |
|
393 (nsGkAtoms::colspan == aAttribute)) { |
|
394 nsTableCellMap* cellMap = GetCellMap(); |
|
395 if (cellMap) { |
|
396 // for now just remove the cell from the map and reinsert it |
|
397 int32_t rowIndex, colIndex; |
|
398 cellFrame->GetRowIndex(rowIndex); |
|
399 cellFrame->GetColIndex(colIndex); |
|
400 RemoveCell(cellFrame, rowIndex); |
|
401 nsAutoTArray<nsTableCellFrame*, 1> cells; |
|
402 cells.AppendElement(cellFrame); |
|
403 InsertCells(cells, rowIndex, colIndex - 1); |
|
404 |
|
405 // XXX Should this use eStyleChange? It currently doesn't need |
|
406 // to, but it might given more optimization. |
|
407 PresContext()->PresShell()-> |
|
408 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY); |
|
409 } |
|
410 } |
|
411 } |
|
412 } |
|
413 |
|
414 |
|
415 /* ****** CellMap methods ******* */ |
|
416 |
|
417 /* return the effective col count */ |
|
418 int32_t nsTableFrame::GetEffectiveColCount() const |
|
419 { |
|
420 int32_t colCount = GetColCount(); |
|
421 if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) { |
|
422 nsTableCellMap* cellMap = GetCellMap(); |
|
423 if (!cellMap) { |
|
424 return 0; |
|
425 } |
|
426 // don't count cols at the end that don't have originating cells |
|
427 for (int32_t colX = colCount - 1; colX >= 0; colX--) { |
|
428 if (cellMap->GetNumCellsOriginatingInCol(colX) > 0) { |
|
429 break; |
|
430 } |
|
431 colCount--; |
|
432 } |
|
433 } |
|
434 return colCount; |
|
435 } |
|
436 |
|
437 int32_t nsTableFrame::GetIndexOfLastRealCol() |
|
438 { |
|
439 int32_t numCols = mColFrames.Length(); |
|
440 if (numCols > 0) { |
|
441 for (int32_t colX = numCols - 1; colX >= 0; colX--) { |
|
442 nsTableColFrame* colFrame = GetColFrame(colX); |
|
443 if (colFrame) { |
|
444 if (eColAnonymousCell != colFrame->GetColType()) { |
|
445 return colX; |
|
446 } |
|
447 } |
|
448 } |
|
449 } |
|
450 return -1; |
|
451 } |
|
452 |
|
453 nsTableColFrame* |
|
454 nsTableFrame::GetColFrame(int32_t aColIndex) const |
|
455 { |
|
456 NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow"); |
|
457 int32_t numCols = mColFrames.Length(); |
|
458 if ((aColIndex >= 0) && (aColIndex < numCols)) { |
|
459 return mColFrames.ElementAt(aColIndex); |
|
460 } |
|
461 else { |
|
462 NS_ERROR("invalid col index"); |
|
463 return nullptr; |
|
464 } |
|
465 } |
|
466 |
|
467 int32_t nsTableFrame::GetEffectiveRowSpan(int32_t aRowIndex, |
|
468 const nsTableCellFrame& aCell) const |
|
469 { |
|
470 nsTableCellMap* cellMap = GetCellMap(); |
|
471 NS_PRECONDITION (nullptr != cellMap, "bad call, cellMap not yet allocated."); |
|
472 |
|
473 int32_t colIndex; |
|
474 aCell.GetColIndex(colIndex); |
|
475 return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex); |
|
476 } |
|
477 |
|
478 int32_t nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell, |
|
479 nsCellMap* aCellMap) |
|
480 { |
|
481 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); |
|
482 |
|
483 int32_t colIndex, rowIndex; |
|
484 aCell.GetColIndex(colIndex); |
|
485 aCell.GetRowIndex(rowIndex); |
|
486 |
|
487 if (aCellMap) |
|
488 return aCellMap->GetRowSpan(rowIndex, colIndex, true); |
|
489 else |
|
490 return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex); |
|
491 } |
|
492 |
|
493 int32_t nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell, |
|
494 nsCellMap* aCellMap) const |
|
495 { |
|
496 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); |
|
497 |
|
498 int32_t colIndex, rowIndex; |
|
499 aCell.GetColIndex(colIndex); |
|
500 aCell.GetRowIndex(rowIndex); |
|
501 bool ignore; |
|
502 |
|
503 if (aCellMap) |
|
504 return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore); |
|
505 else |
|
506 return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex); |
|
507 } |
|
508 |
|
509 bool nsTableFrame::HasMoreThanOneCell(int32_t aRowIndex) const |
|
510 { |
|
511 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1); |
|
512 return tableCellMap->HasMoreThanOneCell(aRowIndex); |
|
513 } |
|
514 |
|
515 void nsTableFrame::AdjustRowIndices(int32_t aRowIndex, |
|
516 int32_t aAdjustment) |
|
517 { |
|
518 // Iterate over the row groups and adjust the row indices of all rows |
|
519 // whose index is >= aRowIndex. |
|
520 RowGroupArray rowGroups; |
|
521 OrderRowGroups(rowGroups); |
|
522 |
|
523 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
524 rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment); |
|
525 } |
|
526 } |
|
527 |
|
528 |
|
529 void nsTableFrame::ResetRowIndices(const nsFrameList::Slice& aRowGroupsToExclude) |
|
530 { |
|
531 // Iterate over the row groups and adjust the row indices of all rows |
|
532 // omit the rowgroups that will be inserted later |
|
533 RowGroupArray rowGroups; |
|
534 OrderRowGroups(rowGroups); |
|
535 |
|
536 int32_t rowIndex = 0; |
|
537 nsTHashtable<nsPtrHashKey<nsTableRowGroupFrame> > excludeRowGroups; |
|
538 nsFrameList::Enumerator excludeRowGroupsEnumerator(aRowGroupsToExclude); |
|
539 while (!excludeRowGroupsEnumerator.AtEnd()) { |
|
540 excludeRowGroups.PutEntry(static_cast<nsTableRowGroupFrame*>(excludeRowGroupsEnumerator.get())); |
|
541 excludeRowGroupsEnumerator.Next(); |
|
542 } |
|
543 |
|
544 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
545 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
546 if (!excludeRowGroups.GetEntry(rgFrame)) { |
|
547 const nsFrameList& rowFrames = rgFrame->PrincipalChildList(); |
|
548 for (nsFrameList::Enumerator rows(rowFrames); !rows.AtEnd(); rows.Next()) { |
|
549 if (NS_STYLE_DISPLAY_TABLE_ROW==rows.get()->StyleDisplay()->mDisplay) { |
|
550 ((nsTableRowFrame *)rows.get())->SetRowIndex(rowIndex); |
|
551 rowIndex++; |
|
552 } |
|
553 } |
|
554 } |
|
555 } |
|
556 } |
|
557 void nsTableFrame::InsertColGroups(int32_t aStartColIndex, |
|
558 const nsFrameList::Slice& aColGroups) |
|
559 { |
|
560 int32_t colIndex = aStartColIndex; |
|
561 nsFrameList::Enumerator colGroups(aColGroups); |
|
562 for (; !colGroups.AtEnd(); colGroups.Next()) { |
|
563 MOZ_ASSERT(colGroups.get()->GetType() == nsGkAtoms::tableColGroupFrame); |
|
564 nsTableColGroupFrame* cgFrame = |
|
565 static_cast<nsTableColGroupFrame*>(colGroups.get()); |
|
566 cgFrame->SetStartColumnIndex(colIndex); |
|
567 // XXXbz this sucks. AddColsToTable will actually remove colgroups from |
|
568 // the list we're traversing! Need to fix things here. :( I guess this is |
|
569 // why the old code used pointer-to-last-frame as opposed to |
|
570 // pointer-to-frame-after-last.... |
|
571 |
|
572 // How about dealing with this by storing a const reference to the |
|
573 // mNextSibling of the framelist's last frame, instead of storing a pointer |
|
574 // to the first-after-next frame? Will involve making nsFrameList friend |
|
575 // of nsIFrame, but it's time for that anyway. |
|
576 cgFrame->AddColsToTable(colIndex, false, |
|
577 colGroups.get()->PrincipalChildList()); |
|
578 int32_t numCols = cgFrame->GetColCount(); |
|
579 colIndex += numCols; |
|
580 } |
|
581 |
|
582 nsFrameList::Enumerator remainingColgroups = colGroups.GetUnlimitedEnumerator(); |
|
583 if (!remainingColgroups.AtEnd()) { |
|
584 nsTableColGroupFrame::ResetColIndices( |
|
585 static_cast<nsTableColGroupFrame*>(remainingColgroups.get()), colIndex); |
|
586 } |
|
587 } |
|
588 |
|
589 void nsTableFrame::InsertCol(nsTableColFrame& aColFrame, |
|
590 int32_t aColIndex) |
|
591 { |
|
592 mColFrames.InsertElementAt(aColIndex, &aColFrame); |
|
593 nsTableColType insertedColType = aColFrame.GetColType(); |
|
594 int32_t numCacheCols = mColFrames.Length(); |
|
595 nsTableCellMap* cellMap = GetCellMap(); |
|
596 if (cellMap) { |
|
597 int32_t numMapCols = cellMap->GetColCount(); |
|
598 if (numCacheCols > numMapCols) { |
|
599 bool removedFromCache = false; |
|
600 if (eColAnonymousCell != insertedColType) { |
|
601 nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1); |
|
602 if (lastCol) { |
|
603 nsTableColType lastColType = lastCol->GetColType(); |
|
604 if (eColAnonymousCell == lastColType) { |
|
605 // remove the col from the cache |
|
606 mColFrames.RemoveElementAt(numCacheCols - 1); |
|
607 // remove the col from the eColGroupAnonymousCell col group |
|
608 nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild(); |
|
609 if (lastColGroup) { |
|
610 lastColGroup->RemoveChild(*lastCol, false); |
|
611 |
|
612 // remove the col group if it is empty |
|
613 if (lastColGroup->GetColCount() <= 0) { |
|
614 mColGroups.DestroyFrame((nsIFrame*)lastColGroup); |
|
615 } |
|
616 } |
|
617 removedFromCache = true; |
|
618 } |
|
619 } |
|
620 } |
|
621 if (!removedFromCache) { |
|
622 cellMap->AddColsAtEnd(1); |
|
623 } |
|
624 } |
|
625 } |
|
626 // for now, just bail and recalc all of the collapsing borders |
|
627 if (IsBorderCollapse()) { |
|
628 nsIntRect damageArea(aColIndex, 0, 1, GetRowCount()); |
|
629 AddBCDamageArea(damageArea); |
|
630 } |
|
631 } |
|
632 |
|
633 void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame, |
|
634 int32_t aColIndex, |
|
635 bool aRemoveFromCache, |
|
636 bool aRemoveFromCellMap) |
|
637 { |
|
638 if (aRemoveFromCache) { |
|
639 mColFrames.RemoveElementAt(aColIndex); |
|
640 } |
|
641 if (aRemoveFromCellMap) { |
|
642 nsTableCellMap* cellMap = GetCellMap(); |
|
643 if (cellMap) { |
|
644 AppendAnonymousColFrames(1); |
|
645 } |
|
646 } |
|
647 // for now, just bail and recalc all of the collapsing borders |
|
648 if (IsBorderCollapse()) { |
|
649 nsIntRect damageArea(0, 0, GetColCount(), GetRowCount()); |
|
650 AddBCDamageArea(damageArea); |
|
651 } |
|
652 } |
|
653 |
|
654 /** Get the cell map for this table frame. It is not always mCellMap. |
|
655 * Only the first-in-flow has a legit cell map. |
|
656 */ |
|
657 nsTableCellMap* |
|
658 nsTableFrame::GetCellMap() const |
|
659 { |
|
660 return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap; |
|
661 } |
|
662 |
|
663 // XXX this needs to be moved to nsCSSFrameConstructor |
|
664 nsTableColGroupFrame* |
|
665 nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType) |
|
666 { |
|
667 nsIContent* colGroupContent = GetContent(); |
|
668 nsPresContext* presContext = PresContext(); |
|
669 nsIPresShell *shell = presContext->PresShell(); |
|
670 |
|
671 nsRefPtr<nsStyleContext> colGroupStyle; |
|
672 colGroupStyle = shell->StyleSet()-> |
|
673 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup, mStyleContext); |
|
674 // Create a col group frame |
|
675 nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle); |
|
676 ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType); |
|
677 newFrame->Init(colGroupContent, this, nullptr); |
|
678 return (nsTableColGroupFrame *)newFrame; |
|
679 } |
|
680 |
|
681 void |
|
682 nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd) |
|
683 { |
|
684 // get the last col group frame |
|
685 nsTableColGroupFrame* colGroupFrame = |
|
686 static_cast<nsTableColGroupFrame*>(mColGroups.LastChild()); |
|
687 |
|
688 if (!colGroupFrame || |
|
689 (colGroupFrame->GetColType() != eColGroupAnonymousCell)) { |
|
690 int32_t colIndex = (colGroupFrame) ? |
|
691 colGroupFrame->GetStartColumnIndex() + |
|
692 colGroupFrame->GetColCount() : 0; |
|
693 colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell); |
|
694 if (!colGroupFrame) { |
|
695 return; |
|
696 } |
|
697 // add the new frame to the child list |
|
698 mColGroups.AppendFrame(this, colGroupFrame); |
|
699 colGroupFrame->SetStartColumnIndex(colIndex); |
|
700 } |
|
701 AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell, |
|
702 true); |
|
703 |
|
704 } |
|
705 |
|
706 // XXX this needs to be moved to nsCSSFrameConstructor |
|
707 // Right now it only creates the col frames at the end |
|
708 void |
|
709 nsTableFrame::AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame, |
|
710 int32_t aNumColsToAdd, |
|
711 nsTableColType aColType, |
|
712 bool aAddToTable) |
|
713 { |
|
714 NS_PRECONDITION(aColGroupFrame, "null frame"); |
|
715 NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen"); |
|
716 |
|
717 nsIPresShell *shell = PresContext()->PresShell(); |
|
718 |
|
719 // Get the last col frame |
|
720 nsFrameList newColFrames; |
|
721 |
|
722 int32_t startIndex = mColFrames.Length(); |
|
723 int32_t lastIndex = startIndex + aNumColsToAdd - 1; |
|
724 |
|
725 for (int32_t childX = startIndex; childX <= lastIndex; childX++) { |
|
726 nsIContent* iContent; |
|
727 nsRefPtr<nsStyleContext> styleContext; |
|
728 nsStyleContext* parentStyleContext; |
|
729 |
|
730 // all anonymous cols that we create here use a pseudo style context of the |
|
731 // col group |
|
732 iContent = aColGroupFrame->GetContent(); |
|
733 parentStyleContext = aColGroupFrame->StyleContext(); |
|
734 styleContext = shell->StyleSet()-> |
|
735 ResolveAnonymousBoxStyle(nsCSSAnonBoxes::tableCol, parentStyleContext); |
|
736 // ASSERTION to check for bug 54454 sneaking back in... |
|
737 NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames"); |
|
738 |
|
739 // create the new col frame |
|
740 nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext); |
|
741 ((nsTableColFrame *) colFrame)->SetColType(aColType); |
|
742 colFrame->Init(iContent, aColGroupFrame, nullptr); |
|
743 |
|
744 newColFrames.AppendFrame(nullptr, colFrame); |
|
745 } |
|
746 nsFrameList& cols = aColGroupFrame->GetWritableChildList(); |
|
747 nsIFrame* oldLastCol = cols.LastChild(); |
|
748 const nsFrameList::Slice& newCols = |
|
749 cols.InsertFrames(nullptr, oldLastCol, newColFrames); |
|
750 if (aAddToTable) { |
|
751 // get the starting col index in the cache |
|
752 int32_t startColIndex; |
|
753 if (oldLastCol) { |
|
754 startColIndex = |
|
755 static_cast<nsTableColFrame*>(oldLastCol)->GetColIndex() + 1; |
|
756 } else { |
|
757 startColIndex = aColGroupFrame->GetStartColumnIndex(); |
|
758 } |
|
759 |
|
760 aColGroupFrame->AddColsToTable(startColIndex, true, newCols); |
|
761 } |
|
762 } |
|
763 |
|
764 void |
|
765 nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap) |
|
766 { |
|
767 int32_t numColsInMap = GetColCount(); |
|
768 int32_t numColsInCache = mColFrames.Length(); |
|
769 int32_t numColsToAdd = numColsInMap - numColsInCache; |
|
770 if (numColsToAdd > 0) { |
|
771 // this sets the child list, updates the col cache and cell map |
|
772 AppendAnonymousColFrames(numColsToAdd); |
|
773 } |
|
774 if (numColsToAdd < 0) { |
|
775 int32_t numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd); |
|
776 // if the cell map has fewer cols than the cache, correct it |
|
777 if (numColsNotRemoved > 0) { |
|
778 aCellMap->AddColsAtEnd(numColsNotRemoved); |
|
779 } |
|
780 } |
|
781 if (numColsToAdd && HasZeroColSpans()) { |
|
782 SetNeedColSpanExpansion(true); |
|
783 } |
|
784 if (NeedColSpanExpansion()) { |
|
785 // This flag can be set in two ways -- either by changing |
|
786 // the number of columns (that happens in the block above), |
|
787 // or by adding a cell with colspan="0" to the cellmap. To |
|
788 // handle the latter case we need to explicitly check the |
|
789 // flag here -- it may be set even if the number of columns |
|
790 // did not change. |
|
791 // |
|
792 // @see nsCellMap::AppendCell |
|
793 |
|
794 aCellMap->ExpandZeroColSpans(); |
|
795 } |
|
796 } |
|
797 |
|
798 void |
|
799 nsTableFrame::DidResizeColumns() |
|
800 { |
|
801 NS_PRECONDITION(!GetPrevInFlow(), |
|
802 "should only be called on first-in-flow"); |
|
803 if (mBits.mResizedColumns) |
|
804 return; // already marked |
|
805 |
|
806 for (nsTableFrame *f = this; f; |
|
807 f = static_cast<nsTableFrame*>(f->GetNextInFlow())) |
|
808 f->mBits.mResizedColumns = true; |
|
809 } |
|
810 |
|
811 void |
|
812 nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame, |
|
813 int32_t aRowIndex) |
|
814 { |
|
815 nsTableCellMap* cellMap = GetCellMap(); |
|
816 if (cellMap) { |
|
817 nsIntRect damageArea(0,0,0,0); |
|
818 cellMap->AppendCell(aCellFrame, aRowIndex, true, damageArea); |
|
819 MatchCellMapToColCache(cellMap); |
|
820 if (IsBorderCollapse()) { |
|
821 AddBCDamageArea(damageArea); |
|
822 } |
|
823 } |
|
824 } |
|
825 |
|
826 void nsTableFrame::InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames, |
|
827 int32_t aRowIndex, |
|
828 int32_t aColIndexBefore) |
|
829 { |
|
830 nsTableCellMap* cellMap = GetCellMap(); |
|
831 if (cellMap) { |
|
832 nsIntRect damageArea(0,0,0,0); |
|
833 cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea); |
|
834 MatchCellMapToColCache(cellMap); |
|
835 if (IsBorderCollapse()) { |
|
836 AddBCDamageArea(damageArea); |
|
837 } |
|
838 } |
|
839 } |
|
840 |
|
841 // this removes the frames from the col group and table, but not the cell map |
|
842 int32_t |
|
843 nsTableFrame::DestroyAnonymousColFrames(int32_t aNumFrames) |
|
844 { |
|
845 // only remove cols that are of type eTypeAnonymous cell (they are at the end) |
|
846 int32_t endIndex = mColFrames.Length() - 1; |
|
847 int32_t startIndex = (endIndex - aNumFrames) + 1; |
|
848 int32_t numColsRemoved = 0; |
|
849 for (int32_t colX = endIndex; colX >= startIndex; colX--) { |
|
850 nsTableColFrame* colFrame = GetColFrame(colX); |
|
851 if (colFrame && (eColAnonymousCell == colFrame->GetColType())) { |
|
852 nsTableColGroupFrame* cgFrame = |
|
853 static_cast<nsTableColGroupFrame*>(colFrame->GetParent()); |
|
854 // remove the frame from the colgroup |
|
855 cgFrame->RemoveChild(*colFrame, false); |
|
856 // remove the frame from the cache, but not the cell map |
|
857 RemoveCol(nullptr, colX, true, false); |
|
858 numColsRemoved++; |
|
859 } |
|
860 else { |
|
861 break; |
|
862 } |
|
863 } |
|
864 return (aNumFrames - numColsRemoved); |
|
865 } |
|
866 |
|
867 void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame, |
|
868 int32_t aRowIndex) |
|
869 { |
|
870 nsTableCellMap* cellMap = GetCellMap(); |
|
871 if (cellMap) { |
|
872 nsIntRect damageArea(0,0,0,0); |
|
873 cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea); |
|
874 MatchCellMapToColCache(cellMap); |
|
875 if (IsBorderCollapse()) { |
|
876 AddBCDamageArea(damageArea); |
|
877 } |
|
878 } |
|
879 } |
|
880 |
|
881 int32_t |
|
882 nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame* aRowGroupFrame) |
|
883 { |
|
884 RowGroupArray orderedRowGroups; |
|
885 OrderRowGroups(orderedRowGroups); |
|
886 |
|
887 int32_t rowIndex = 0; |
|
888 for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
|
889 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; |
|
890 if (rgFrame == aRowGroupFrame) { |
|
891 break; |
|
892 } |
|
893 int32_t numRows = rgFrame->GetRowCount(); |
|
894 rowIndex += numRows; |
|
895 } |
|
896 return rowIndex; |
|
897 } |
|
898 |
|
899 // this cannot extend beyond a single row group |
|
900 void nsTableFrame::AppendRows(nsTableRowGroupFrame* aRowGroupFrame, |
|
901 int32_t aRowIndex, |
|
902 nsTArray<nsTableRowFrame*>& aRowFrames) |
|
903 { |
|
904 nsTableCellMap* cellMap = GetCellMap(); |
|
905 if (cellMap) { |
|
906 int32_t absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex; |
|
907 InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, true); |
|
908 } |
|
909 } |
|
910 |
|
911 // this cannot extend beyond a single row group |
|
912 int32_t |
|
913 nsTableFrame::InsertRows(nsTableRowGroupFrame* aRowGroupFrame, |
|
914 nsTArray<nsTableRowFrame*>& aRowFrames, |
|
915 int32_t aRowIndex, |
|
916 bool aConsiderSpans) |
|
917 { |
|
918 #ifdef DEBUG_TABLE_CELLMAP |
|
919 printf("=== insertRowsBefore firstRow=%d \n", aRowIndex); |
|
920 Dump(true, false, true); |
|
921 #endif |
|
922 |
|
923 int32_t numColsToAdd = 0; |
|
924 nsTableCellMap* cellMap = GetCellMap(); |
|
925 if (cellMap) { |
|
926 nsIntRect damageArea(0,0,0,0); |
|
927 int32_t origNumRows = cellMap->GetRowCount(); |
|
928 int32_t numNewRows = aRowFrames.Length(); |
|
929 cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea); |
|
930 MatchCellMapToColCache(cellMap); |
|
931 if (aRowIndex < origNumRows) { |
|
932 AdjustRowIndices(aRowIndex, numNewRows); |
|
933 } |
|
934 // assign the correct row indices to the new rows. If they were adjusted above |
|
935 // it may not have been done correctly because each row is constructed with index 0 |
|
936 for (int32_t rowY = 0; rowY < numNewRows; rowY++) { |
|
937 nsTableRowFrame* rowFrame = aRowFrames.ElementAt(rowY); |
|
938 rowFrame->SetRowIndex(aRowIndex + rowY); |
|
939 } |
|
940 if (IsBorderCollapse()) { |
|
941 AddBCDamageArea(damageArea); |
|
942 } |
|
943 } |
|
944 #ifdef DEBUG_TABLE_CELLMAP |
|
945 printf("=== insertRowsAfter \n"); |
|
946 Dump(true, false, true); |
|
947 #endif |
|
948 |
|
949 return numColsToAdd; |
|
950 } |
|
951 |
|
952 // this cannot extend beyond a single row group |
|
953 void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame, |
|
954 int32_t aNumRowsToRemove, |
|
955 bool aConsiderSpans) |
|
956 { |
|
957 #ifdef TBD_OPTIMIZATION |
|
958 // decide if we need to rebalance. we have to do this here because the row group |
|
959 // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed |
|
960 bool stopTelling = false; |
|
961 for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking); |
|
962 kidFrame = kidFrame->GetNextSibling()) { |
|
963 nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame); |
|
964 if (cellFrame) { |
|
965 stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(), |
|
966 cellFrame->GetMaximumWidth(), true); |
|
967 } |
|
968 } |
|
969 // XXX need to consider what happens if there are cells that have rowspans |
|
970 // into the deleted row. Need to consider moving rows if a rebalance doesn't happen |
|
971 #endif |
|
972 |
|
973 int32_t firstRowIndex = aFirstRowFrame.GetRowIndex(); |
|
974 #ifdef DEBUG_TABLE_CELLMAP |
|
975 printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove); |
|
976 Dump(true, false, true); |
|
977 #endif |
|
978 nsTableCellMap* cellMap = GetCellMap(); |
|
979 if (cellMap) { |
|
980 nsIntRect damageArea(0,0,0,0); |
|
981 cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea); |
|
982 MatchCellMapToColCache(cellMap); |
|
983 if (IsBorderCollapse()) { |
|
984 AddBCDamageArea(damageArea); |
|
985 } |
|
986 } |
|
987 AdjustRowIndices(firstRowIndex, -aNumRowsToRemove); |
|
988 #ifdef DEBUG_TABLE_CELLMAP |
|
989 printf("=== removeRowsAfter\n"); |
|
990 Dump(true, true, true); |
|
991 #endif |
|
992 } |
|
993 |
|
994 // collect the rows ancestors of aFrame |
|
995 int32_t |
|
996 nsTableFrame::CollectRows(nsIFrame* aFrame, |
|
997 nsTArray<nsTableRowFrame*>& aCollection) |
|
998 { |
|
999 NS_PRECONDITION(aFrame, "null frame"); |
|
1000 int32_t numRows = 0; |
|
1001 nsIFrame* childFrame = aFrame->GetFirstPrincipalChild(); |
|
1002 while (childFrame) { |
|
1003 aCollection.AppendElement(static_cast<nsTableRowFrame*>(childFrame)); |
|
1004 numRows++; |
|
1005 childFrame = childFrame->GetNextSibling(); |
|
1006 } |
|
1007 return numRows; |
|
1008 } |
|
1009 |
|
1010 void |
|
1011 nsTableFrame::InsertRowGroups(const nsFrameList::Slice& aRowGroups) |
|
1012 { |
|
1013 #ifdef DEBUG_TABLE_CELLMAP |
|
1014 printf("=== insertRowGroupsBefore\n"); |
|
1015 Dump(true, false, true); |
|
1016 #endif |
|
1017 nsTableCellMap* cellMap = GetCellMap(); |
|
1018 if (cellMap) { |
|
1019 RowGroupArray orderedRowGroups; |
|
1020 OrderRowGroups(orderedRowGroups); |
|
1021 |
|
1022 nsAutoTArray<nsTableRowFrame*, 8> rows; |
|
1023 // Loop over the rowgroups and check if some of them are new, if they are |
|
1024 // insert cellmaps in the order that is predefined by OrderRowGroups, |
|
1025 // XXXbz this code is O(N*M) where N is number of new rowgroups |
|
1026 // and M is number of rowgroups we have! |
|
1027 uint32_t rgIndex; |
|
1028 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
|
1029 for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd(); |
|
1030 rowgroups.Next()) { |
|
1031 if (orderedRowGroups[rgIndex] == rowgroups.get()) { |
|
1032 nsTableRowGroupFrame* priorRG = |
|
1033 (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1]; |
|
1034 // create and add the cell map for the row group |
|
1035 cellMap->InsertGroupCellMap(orderedRowGroups[rgIndex], priorRG); |
|
1036 |
|
1037 break; |
|
1038 } |
|
1039 } |
|
1040 } |
|
1041 cellMap->Synchronize(this); |
|
1042 ResetRowIndices(aRowGroups); |
|
1043 |
|
1044 //now that the cellmaps are reordered too insert the rows |
|
1045 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
|
1046 for (nsFrameList::Enumerator rowgroups(aRowGroups); !rowgroups.AtEnd(); |
|
1047 rowgroups.Next()) { |
|
1048 if (orderedRowGroups[rgIndex] == rowgroups.get()) { |
|
1049 nsTableRowGroupFrame* priorRG = |
|
1050 (0 == rgIndex) ? nullptr : orderedRowGroups[rgIndex - 1]; |
|
1051 // collect the new row frames in an array and add them to the table |
|
1052 int32_t numRows = CollectRows(rowgroups.get(), rows); |
|
1053 if (numRows > 0) { |
|
1054 int32_t rowIndex = 0; |
|
1055 if (priorRG) { |
|
1056 int32_t priorNumRows = priorRG->GetRowCount(); |
|
1057 rowIndex = priorRG->GetStartRowIndex() + priorNumRows; |
|
1058 } |
|
1059 InsertRows(orderedRowGroups[rgIndex], rows, rowIndex, true); |
|
1060 rows.Clear(); |
|
1061 } |
|
1062 break; |
|
1063 } |
|
1064 } |
|
1065 } |
|
1066 |
|
1067 } |
|
1068 #ifdef DEBUG_TABLE_CELLMAP |
|
1069 printf("=== insertRowGroupsAfter\n"); |
|
1070 Dump(true, true, true); |
|
1071 #endif |
|
1072 } |
|
1073 |
|
1074 |
|
1075 ///////////////////////////////////////////////////////////////////////////// |
|
1076 // Child frame enumeration |
|
1077 |
|
1078 const nsFrameList& |
|
1079 nsTableFrame::GetChildList(ChildListID aListID) const |
|
1080 { |
|
1081 if (aListID == kColGroupList) { |
|
1082 return mColGroups; |
|
1083 } |
|
1084 return nsContainerFrame::GetChildList(aListID); |
|
1085 } |
|
1086 |
|
1087 void |
|
1088 nsTableFrame::GetChildLists(nsTArray<ChildList>* aLists) const |
|
1089 { |
|
1090 nsContainerFrame::GetChildLists(aLists); |
|
1091 mColGroups.AppendIfNonempty(aLists, kColGroupList); |
|
1092 } |
|
1093 |
|
1094 nsRect |
|
1095 nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder, bool* aSnap) { |
|
1096 *aSnap = false; |
|
1097 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); |
|
1098 } |
|
1099 |
|
1100 bool |
|
1101 nsDisplayTableItem::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder, |
|
1102 nsIFrame* aFrame) |
|
1103 { |
|
1104 if (!mPartHasFixedBackground) |
|
1105 return false; |
|
1106 |
|
1107 // If aFrame is mFrame or an ancestor in this document, and aFrame is |
|
1108 // not the viewport frame, then moving aFrame will move mFrame |
|
1109 // relative to the viewport, so our fixed-pos background will change. |
|
1110 return mFrame == aFrame || |
|
1111 nsLayoutUtils::IsProperAncestorFrame(aFrame, mFrame); |
|
1112 } |
|
1113 |
|
1114 /* static */ void |
|
1115 nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame) |
|
1116 { |
|
1117 nsStyleContext *bgSC; |
|
1118 if (!nsCSSRendering::FindBackground(aFrame, &bgSC)) |
|
1119 return; |
|
1120 if (!bgSC->StyleBackground()->HasFixedBackground()) |
|
1121 return; |
|
1122 |
|
1123 mPartHasFixedBackground = true; |
|
1124 } |
|
1125 |
|
1126 class nsDisplayTableBorderBackground : public nsDisplayTableItem { |
|
1127 public: |
|
1128 nsDisplayTableBorderBackground(nsDisplayListBuilder* aBuilder, |
|
1129 nsTableFrame* aFrame) : |
|
1130 nsDisplayTableItem(aBuilder, aFrame) { |
|
1131 MOZ_COUNT_CTOR(nsDisplayTableBorderBackground); |
|
1132 } |
|
1133 #ifdef NS_BUILD_REFCNT_LOGGING |
|
1134 virtual ~nsDisplayTableBorderBackground() { |
|
1135 MOZ_COUNT_DTOR(nsDisplayTableBorderBackground); |
|
1136 } |
|
1137 #endif |
|
1138 |
|
1139 virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
1140 const nsDisplayItemGeometry* aGeometry, |
|
1141 nsRegion *aInvalidRegion) MOZ_OVERRIDE; |
|
1142 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
1143 nsRenderingContext* aCtx) MOZ_OVERRIDE; |
|
1144 NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND) |
|
1145 }; |
|
1146 |
|
1147 #ifdef DEBUG |
|
1148 static bool |
|
1149 IsFrameAllowedInTable(nsIAtom* aType) |
|
1150 { |
|
1151 return IS_TABLE_CELL(aType) || |
|
1152 nsGkAtoms::tableRowFrame == aType || |
|
1153 nsGkAtoms::tableRowGroupFrame == aType || |
|
1154 nsGkAtoms::scrollFrame == aType || |
|
1155 nsGkAtoms::tableFrame == aType || |
|
1156 nsGkAtoms::tableColFrame == aType || |
|
1157 nsGkAtoms::tableColGroupFrame == aType; |
|
1158 } |
|
1159 #endif |
|
1160 |
|
1161 /* static */ bool |
|
1162 nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart, |
|
1163 nsIFrame* aEnd) |
|
1164 { |
|
1165 for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) { |
|
1166 NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type"); |
|
1167 |
|
1168 if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(f)) |
|
1169 return true; |
|
1170 |
|
1171 nsTableCellFrame *cellFrame = do_QueryFrame(f); |
|
1172 if (cellFrame) |
|
1173 continue; |
|
1174 |
|
1175 if (AnyTablePartHasUndecodedBackgroundImage(f->PrincipalChildList().FirstChild(), nullptr)) |
|
1176 return true; |
|
1177 } |
|
1178 |
|
1179 return false; |
|
1180 } |
|
1181 |
|
1182 void |
|
1183 nsDisplayTableBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
1184 const nsDisplayItemGeometry* aGeometry, |
|
1185 nsRegion *aInvalidRegion) |
|
1186 { |
|
1187 if (aBuilder->ShouldSyncDecodeImages()) { |
|
1188 if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling()) || |
|
1189 nsTableFrame::AnyTablePartHasUndecodedBackgroundImage( |
|
1190 mFrame->GetChildList(nsIFrame::kColGroupList).FirstChild(), nullptr)) { |
|
1191 bool snap; |
|
1192 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
|
1193 } |
|
1194 } |
|
1195 |
|
1196 nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
|
1197 } |
|
1198 |
|
1199 void |
|
1200 nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder, |
|
1201 nsRenderingContext* aCtx) |
|
1202 { |
|
1203 static_cast<nsTableFrame*>(mFrame)-> |
|
1204 PaintTableBorderBackground(*aCtx, mVisibleRect, |
|
1205 ToReferenceFrame(), |
|
1206 aBuilder->GetBackgroundPaintFlags()); |
|
1207 } |
|
1208 |
|
1209 static int32_t GetTablePartRank(nsDisplayItem* aItem) |
|
1210 { |
|
1211 nsIAtom* type = aItem->Frame()->GetType(); |
|
1212 if (type == nsGkAtoms::tableFrame) |
|
1213 return 0; |
|
1214 if (type == nsGkAtoms::tableRowGroupFrame) |
|
1215 return 1; |
|
1216 if (type == nsGkAtoms::tableRowFrame) |
|
1217 return 2; |
|
1218 return 3; |
|
1219 } |
|
1220 |
|
1221 static bool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem2, |
|
1222 void* aClosure) |
|
1223 { |
|
1224 return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2); |
|
1225 } |
|
1226 |
|
1227 /* static */ void |
|
1228 nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame, |
|
1229 const nsRect& aDirtyRect, const nsDisplayListSet& aLists) |
|
1230 { |
|
1231 // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren |
|
1232 // does, except that we allow the children's background and borders to go |
|
1233 // in our BorderBackground list. This doesn't really affect background |
|
1234 // painting --- the children won't actually draw their own backgrounds |
|
1235 // because the nsTableFrame already drew them, unless a child has its own |
|
1236 // stacking context, in which case the child won't use its passed-in |
|
1237 // BorderBackground list anyway. It does affect cell borders though; this |
|
1238 // lets us get cell borders into the nsTableFrame's BorderBackground list. |
|
1239 nsIFrame* kid = aFrame->GetFirstPrincipalChild(); |
|
1240 while (kid) { |
|
1241 aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists); |
|
1242 kid = kid->GetNextSibling(); |
|
1243 } |
|
1244 } |
|
1245 |
|
1246 /* static */ void |
|
1247 nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder, |
|
1248 nsFrame* aFrame, |
|
1249 const nsRect& aDirtyRect, |
|
1250 const nsDisplayListSet& aLists, |
|
1251 nsDisplayTableItem* aDisplayItem, |
|
1252 DisplayGenericTablePartTraversal aTraversal) |
|
1253 { |
|
1254 nsDisplayList eventsBorderBackground; |
|
1255 // If we need to sort the event backgrounds, then we'll put descendants' |
|
1256 // display items into their own set of lists. |
|
1257 bool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery(); |
|
1258 nsDisplayListCollection separatedCollection; |
|
1259 const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists; |
|
1260 |
|
1261 nsAutoPushCurrentTableItem pushTableItem; |
|
1262 if (aDisplayItem) { |
|
1263 pushTableItem.Push(aBuilder, aDisplayItem); |
|
1264 } |
|
1265 |
|
1266 if (aFrame->IsVisibleForPainting(aBuilder)) { |
|
1267 nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem(); |
|
1268 // currentItem may be null, when none of the table parts have a |
|
1269 // background or border |
|
1270 if (currentItem) { |
|
1271 currentItem->UpdateForFrameBackground(aFrame); |
|
1272 } |
|
1273 |
|
1274 // Paint the outset box-shadows for the table frames |
|
1275 bool hasBoxShadow = aFrame->StyleBorder()->mBoxShadow != nullptr; |
|
1276 if (hasBoxShadow) { |
|
1277 lists->BorderBackground()->AppendNewToTop( |
|
1278 new (aBuilder) nsDisplayBoxShadowOuter(aBuilder, aFrame)); |
|
1279 } |
|
1280 |
|
1281 // Create dedicated background display items per-frame when we're |
|
1282 // handling events. |
|
1283 // XXX how to handle collapsed borders? |
|
1284 if (aBuilder->IsForEventDelivery()) { |
|
1285 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(aBuilder, aFrame, |
|
1286 lists->BorderBackground()); |
|
1287 } |
|
1288 |
|
1289 // Paint the inset box-shadows for the table frames |
|
1290 if (hasBoxShadow) { |
|
1291 lists->BorderBackground()->AppendNewToTop( |
|
1292 new (aBuilder) nsDisplayBoxShadowInner(aBuilder, aFrame)); |
|
1293 } |
|
1294 } |
|
1295 |
|
1296 aTraversal(aBuilder, aFrame, aDirtyRect, *lists); |
|
1297 |
|
1298 if (sortEventBackgrounds) { |
|
1299 // Ensure that the table frame event background goes before the |
|
1300 // table rowgroups event backgrounds, before the table row event backgrounds, |
|
1301 // before everything else (cells and their blocks) |
|
1302 separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nullptr); |
|
1303 separatedCollection.MoveTo(aLists); |
|
1304 } |
|
1305 |
|
1306 aFrame->DisplayOutline(aBuilder, aLists); |
|
1307 } |
|
1308 |
|
1309 static bool |
|
1310 AnyTablePartHasBorderOrBackground(nsIFrame* aStart, nsIFrame* aEnd) |
|
1311 { |
|
1312 for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) { |
|
1313 NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type"); |
|
1314 |
|
1315 if (FrameHasBorderOrBackground(f)) |
|
1316 return true; |
|
1317 |
|
1318 nsTableCellFrame *cellFrame = do_QueryFrame(f); |
|
1319 if (cellFrame) |
|
1320 continue; |
|
1321 |
|
1322 if (AnyTablePartHasBorderOrBackground(f->PrincipalChildList().FirstChild(), nullptr)) |
|
1323 return true; |
|
1324 } |
|
1325 |
|
1326 return false; |
|
1327 } |
|
1328 |
|
1329 // table paint code is concerned primarily with borders and bg color |
|
1330 // SEC: TODO: adjust the rect for captions |
|
1331 void |
|
1332 nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
1333 const nsRect& aDirtyRect, |
|
1334 const nsDisplayListSet& aLists) |
|
1335 { |
|
1336 DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255)); |
|
1337 |
|
1338 nsDisplayTableItem* item = nullptr; |
|
1339 if (IsVisibleInSelection(aBuilder)) { |
|
1340 if (StyleVisibility()->IsVisible()) { |
|
1341 nsMargin deflate = GetDeflationForBackground(PresContext()); |
|
1342 // If 'deflate' is (0,0,0,0) then we can paint the table background |
|
1343 // in its own display item, so do that to take advantage of |
|
1344 // opacity and visibility optimizations |
|
1345 if (deflate == nsMargin(0, 0, 0, 0)) { |
|
1346 DisplayBackgroundUnconditional(aBuilder, aLists, false); |
|
1347 } |
|
1348 } |
|
1349 |
|
1350 // This background is created if any of the table parts are visible, |
|
1351 // or if we're doing event handling (since DisplayGenericTablePart |
|
1352 // needs the item for the |sortEventBackgrounds|-dependent code). |
|
1353 // Specific visibility decisions are delegated to the table background |
|
1354 // painter, which handles borders and backgrounds for the table. |
|
1355 if (aBuilder->IsForEventDelivery() || |
|
1356 AnyTablePartHasBorderOrBackground(this, GetNextSibling()) || |
|
1357 AnyTablePartHasBorderOrBackground(mColGroups.FirstChild(), nullptr)) { |
|
1358 item = new (aBuilder) nsDisplayTableBorderBackground(aBuilder, this); |
|
1359 aLists.BorderBackground()->AppendNewToTop(item); |
|
1360 } |
|
1361 } |
|
1362 DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item); |
|
1363 } |
|
1364 |
|
1365 nsMargin |
|
1366 nsTableFrame::GetDeflationForBackground(nsPresContext* aPresContext) const |
|
1367 { |
|
1368 if (eCompatibility_NavQuirks != aPresContext->CompatibilityMode() || |
|
1369 !IsBorderCollapse()) |
|
1370 return nsMargin(0,0,0,0); |
|
1371 |
|
1372 return GetOuterBCBorder(); |
|
1373 } |
|
1374 |
|
1375 // XXX We don't put the borders and backgrounds in tree order like we should. |
|
1376 // That requires some major surgery which we aren't going to do right now. |
|
1377 void |
|
1378 nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext, |
|
1379 const nsRect& aDirtyRect, |
|
1380 nsPoint aPt, uint32_t aBGPaintFlags) |
|
1381 { |
|
1382 nsPresContext* presContext = PresContext(); |
|
1383 |
|
1384 TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table, |
|
1385 presContext, aRenderingContext, |
|
1386 aDirtyRect, aPt, aBGPaintFlags); |
|
1387 nsMargin deflate = GetDeflationForBackground(presContext); |
|
1388 // If 'deflate' is (0,0,0,0) then we'll paint the table background |
|
1389 // in a separate display item, so don't do it here. |
|
1390 nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0)); |
|
1391 if (NS_FAILED(rv)) return; |
|
1392 |
|
1393 if (StyleVisibility()->IsVisible()) { |
|
1394 if (!IsBorderCollapse()) { |
|
1395 int skipSides = GetSkipSides(); |
|
1396 nsRect rect(aPt, mRect.Size()); |
|
1397 nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, |
|
1398 aDirtyRect, rect, mStyleContext, skipSides); |
|
1399 } |
|
1400 else { |
|
1401 // XXX we should probably get rid of this translation at some stage |
|
1402 // But that would mean modifying PaintBCBorders, ugh |
|
1403 nsRenderingContext::AutoPushTranslation translate(&aRenderingContext, aPt); |
|
1404 PaintBCBorders(aRenderingContext, aDirtyRect - aPt); |
|
1405 } |
|
1406 } |
|
1407 } |
|
1408 |
|
1409 int |
|
1410 nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const |
|
1411 { |
|
1412 int skip = 0; |
|
1413 // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto |
|
1414 // account for pagination |
|
1415 if (nullptr != GetPrevInFlow()) { |
|
1416 skip |= LOGICAL_SIDE_B_START; |
|
1417 } |
|
1418 if (nullptr != GetNextInFlow()) { |
|
1419 skip |= LOGICAL_SIDE_B_END; |
|
1420 } |
|
1421 return skip; |
|
1422 } |
|
1423 |
|
1424 void |
|
1425 nsTableFrame::SetColumnDimensions(nscoord aHeight, |
|
1426 const nsMargin& aBorderPadding) |
|
1427 { |
|
1428 nscoord cellSpacingX = GetCellSpacingX(); |
|
1429 nscoord cellSpacingY = GetCellSpacingY(); |
|
1430 nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom + |
|
1431 2* cellSpacingY; |
|
1432 |
|
1433 nsTableIterator iter(mColGroups); |
|
1434 nsIFrame* colGroupFrame = iter.First(); |
|
1435 bool tableIsLTR = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; |
|
1436 int32_t colX =tableIsLTR ? 0 : std::max(0, GetColCount() - 1); |
|
1437 int32_t tableColIncr = tableIsLTR ? 1 : -1; |
|
1438 nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX, |
|
1439 aBorderPadding.top + cellSpacingY); |
|
1440 while (colGroupFrame) { |
|
1441 MOZ_ASSERT(colGroupFrame->GetType() == nsGkAtoms::tableColGroupFrame); |
|
1442 nscoord colGroupWidth = 0; |
|
1443 nsTableIterator iterCol(*colGroupFrame); |
|
1444 nsIFrame* colFrame = iterCol.First(); |
|
1445 nsPoint colOrigin(0,0); |
|
1446 while (colFrame) { |
|
1447 if (NS_STYLE_DISPLAY_TABLE_COLUMN == |
|
1448 colFrame->StyleDisplay()->mDisplay) { |
|
1449 NS_ASSERTION(colX < GetColCount(), "invalid number of columns"); |
|
1450 nscoord colWidth = GetColumnWidth(colX); |
|
1451 nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight); |
|
1452 colFrame->SetRect(colRect); |
|
1453 colOrigin.x += colWidth + cellSpacingX; |
|
1454 colGroupWidth += colWidth + cellSpacingX; |
|
1455 colX += tableColIncr; |
|
1456 } |
|
1457 colFrame = iterCol.Next(); |
|
1458 } |
|
1459 if (colGroupWidth) { |
|
1460 colGroupWidth -= cellSpacingX; |
|
1461 } |
|
1462 |
|
1463 nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight); |
|
1464 colGroupFrame->SetRect(colGroupRect); |
|
1465 colGroupFrame = iter.Next(); |
|
1466 colGroupOrigin.x += colGroupWidth + cellSpacingX; |
|
1467 } |
|
1468 } |
|
1469 |
|
1470 // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages. |
|
1471 |
|
1472 // XXX this could be made more general to handle row modifications that change the |
|
1473 // table height, but first we need to scrutinize every Invalidate |
|
1474 void |
|
1475 nsTableFrame::ProcessRowInserted(nscoord aNewHeight) |
|
1476 { |
|
1477 SetRowInserted(false); // reset the bit that got us here |
|
1478 nsTableFrame::RowGroupArray rowGroups; |
|
1479 OrderRowGroups(rowGroups); |
|
1480 // find the row group containing the inserted row |
|
1481 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
1482 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
1483 NS_ASSERTION(rgFrame, "Must have rgFrame here"); |
|
1484 nsIFrame* childFrame = rgFrame->GetFirstPrincipalChild(); |
|
1485 // find the row that was inserted first |
|
1486 while (childFrame) { |
|
1487 nsTableRowFrame *rowFrame = do_QueryFrame(childFrame); |
|
1488 if (rowFrame) { |
|
1489 if (rowFrame->IsFirstInserted()) { |
|
1490 rowFrame->SetFirstInserted(false); |
|
1491 // damage the table from the 1st row inserted to the end of the table |
|
1492 nsIFrame::InvalidateFrame(); |
|
1493 // XXXbz didn't we do this up front? Why do we need to do it again? |
|
1494 SetRowInserted(false); |
|
1495 return; // found it, so leave |
|
1496 } |
|
1497 } |
|
1498 childFrame = childFrame->GetNextSibling(); |
|
1499 } |
|
1500 } |
|
1501 } |
|
1502 |
|
1503 /* virtual */ void |
|
1504 nsTableFrame::MarkIntrinsicWidthsDirty() |
|
1505 { |
|
1506 nsITableLayoutStrategy* tls = LayoutStrategy(); |
|
1507 if (MOZ_UNLIKELY(!tls)) { |
|
1508 // This is a FrameNeedsReflow() from nsBlockFrame::RemoveFrame() |
|
1509 // walking up the ancestor chain in a table next-in-flow. In this case |
|
1510 // our original first-in-flow (which owns the TableLayoutStrategy) has |
|
1511 // already been destroyed and unhooked from the flow chain and thusly |
|
1512 // LayoutStrategy() returns null. All the frames in the flow will be |
|
1513 // destroyed so no need to mark anything dirty here. See bug 595758. |
|
1514 return; |
|
1515 } |
|
1516 tls->MarkIntrinsicWidthsDirty(); |
|
1517 |
|
1518 // XXXldb Call SetBCDamageArea? |
|
1519 |
|
1520 nsContainerFrame::MarkIntrinsicWidthsDirty(); |
|
1521 } |
|
1522 |
|
1523 /* virtual */ nscoord |
|
1524 nsTableFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
1525 { |
|
1526 if (NeedToCalcBCBorders()) |
|
1527 CalcBCBorders(); |
|
1528 |
|
1529 ReflowColGroups(aRenderingContext); |
|
1530 |
|
1531 return LayoutStrategy()->GetMinWidth(aRenderingContext); |
|
1532 } |
|
1533 |
|
1534 /* virtual */ nscoord |
|
1535 nsTableFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
1536 { |
|
1537 if (NeedToCalcBCBorders()) |
|
1538 CalcBCBorders(); |
|
1539 |
|
1540 ReflowColGroups(aRenderingContext); |
|
1541 |
|
1542 return LayoutStrategy()->GetPrefWidth(aRenderingContext, false); |
|
1543 } |
|
1544 |
|
1545 /* virtual */ nsIFrame::IntrinsicWidthOffsetData |
|
1546 nsTableFrame::IntrinsicWidthOffsets(nsRenderingContext* aRenderingContext) |
|
1547 { |
|
1548 IntrinsicWidthOffsetData result = |
|
1549 nsContainerFrame::IntrinsicWidthOffsets(aRenderingContext); |
|
1550 |
|
1551 result.hMargin = 0; |
|
1552 result.hPctMargin = 0; |
|
1553 |
|
1554 if (IsBorderCollapse()) { |
|
1555 result.hPadding = 0; |
|
1556 result.hPctPadding = 0; |
|
1557 |
|
1558 nsMargin outerBC = GetIncludedOuterBCBorder(); |
|
1559 result.hBorder = outerBC.LeftRight(); |
|
1560 } |
|
1561 |
|
1562 return result; |
|
1563 } |
|
1564 |
|
1565 /* virtual */ nsSize |
|
1566 nsTableFrame::ComputeSize(nsRenderingContext *aRenderingContext, |
|
1567 nsSize aCBSize, nscoord aAvailableWidth, |
|
1568 nsSize aMargin, nsSize aBorder, nsSize aPadding, |
|
1569 uint32_t aFlags) |
|
1570 { |
|
1571 nsSize result = |
|
1572 nsContainerFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth, |
|
1573 aMargin, aBorder, aPadding, aFlags); |
|
1574 |
|
1575 // If we're a container for font size inflation, then shrink |
|
1576 // wrapping inside of us should not apply font size inflation. |
|
1577 AutoMaybeDisableFontInflation an(this); |
|
1578 |
|
1579 // Tables never shrink below their min width. |
|
1580 nscoord minWidth = GetMinWidth(aRenderingContext); |
|
1581 if (minWidth > result.width) |
|
1582 result.width = minWidth; |
|
1583 |
|
1584 return result; |
|
1585 } |
|
1586 |
|
1587 nscoord |
|
1588 nsTableFrame::TableShrinkWidthToFit(nsRenderingContext *aRenderingContext, |
|
1589 nscoord aWidthInCB) |
|
1590 { |
|
1591 // If we're a container for font size inflation, then shrink |
|
1592 // wrapping inside of us should not apply font size inflation. |
|
1593 AutoMaybeDisableFontInflation an(this); |
|
1594 |
|
1595 nscoord result; |
|
1596 nscoord minWidth = GetMinWidth(aRenderingContext); |
|
1597 if (minWidth > aWidthInCB) { |
|
1598 result = minWidth; |
|
1599 } else { |
|
1600 // Tables shrink width to fit with a slightly different algorithm |
|
1601 // from the one they use for their intrinsic widths (the difference |
|
1602 // relates to handling of percentage widths on columns). So this |
|
1603 // function differs from nsFrame::ShrinkWidthToFit by only the |
|
1604 // following line. |
|
1605 // Since we've already called GetMinWidth, we don't need to do any |
|
1606 // of the other stuff GetPrefWidth does. |
|
1607 nscoord prefWidth = |
|
1608 LayoutStrategy()->GetPrefWidth(aRenderingContext, true); |
|
1609 if (prefWidth > aWidthInCB) { |
|
1610 result = aWidthInCB; |
|
1611 } else { |
|
1612 result = prefWidth; |
|
1613 } |
|
1614 } |
|
1615 return result; |
|
1616 } |
|
1617 |
|
1618 /* virtual */ nsSize |
|
1619 nsTableFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, |
|
1620 nsSize aCBSize, nscoord aAvailableWidth, |
|
1621 nsSize aMargin, nsSize aBorder, nsSize aPadding, |
|
1622 bool aShrinkWrap) |
|
1623 { |
|
1624 // Tables always shrink-wrap. |
|
1625 nscoord cbBased = aAvailableWidth - aMargin.width - aBorder.width - |
|
1626 aPadding.width; |
|
1627 return nsSize(TableShrinkWidthToFit(aRenderingContext, cbBased), |
|
1628 NS_UNCONSTRAINEDSIZE); |
|
1629 } |
|
1630 |
|
1631 // Return true if aParentReflowState.frame or any of its ancestors within |
|
1632 // the containing table have non-auto height. (e.g. pct or fixed height) |
|
1633 bool |
|
1634 nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState) |
|
1635 { |
|
1636 for (const nsHTMLReflowState* rs = &aParentReflowState; |
|
1637 rs && rs->frame; rs = rs->parentReflowState) { |
|
1638 nsIAtom* frameType = rs->frame->GetType(); |
|
1639 if (IS_TABLE_CELL(frameType) || |
|
1640 (nsGkAtoms::tableRowFrame == frameType) || |
|
1641 (nsGkAtoms::tableRowGroupFrame == frameType)) { |
|
1642 const nsStyleCoord &height = rs->mStylePosition->mHeight; |
|
1643 // calc() with percentages treated like 'auto' on internal table elements |
|
1644 if (height.GetUnit() != eStyleUnit_Auto && |
|
1645 (!height.IsCalcUnit() || !height.HasPercent())) { |
|
1646 return true; |
|
1647 } |
|
1648 } |
|
1649 else if (nsGkAtoms::tableFrame == frameType) { |
|
1650 // we reached the containing table, so always return |
|
1651 return rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto; |
|
1652 } |
|
1653 } |
|
1654 return false; |
|
1655 } |
|
1656 |
|
1657 // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow |
|
1658 void |
|
1659 nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) |
|
1660 { |
|
1661 NS_ASSERTION(IS_TABLE_CELL(aReflowState.frame->GetType()) || |
|
1662 aReflowState.frame->GetType() == nsGkAtoms::tableRowFrame || |
|
1663 aReflowState.frame->GetType() == nsGkAtoms::tableRowGroupFrame || |
|
1664 aReflowState.frame->GetType() == nsGkAtoms::tableFrame, |
|
1665 "unexpected frame type"); |
|
1666 if (!aReflowState.frame->GetPrevInFlow() && // 1st in flow |
|
1667 (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() || // no computed height |
|
1668 0 == aReflowState.ComputedHeight()) && |
|
1669 eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height |
|
1670 nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) { |
|
1671 nsTableFrame::RequestSpecialHeightReflow(aReflowState); |
|
1672 } |
|
1673 } |
|
1674 |
|
1675 // Notify the frame and its ancestors (up to the containing table) that a special |
|
1676 // height reflow will occur. During a special height reflow, a table, row group, |
|
1677 // row, or cell returns the last size it was reflowed at. However, the table may |
|
1678 // change the height of row groups, rows, cells in DistributeHeightToRows after. |
|
1679 // And the row group can change the height of rows, cells in CalculateRowHeights. |
|
1680 void |
|
1681 nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState) |
|
1682 { |
|
1683 // notify the frame and its ancestors of the special reflow, stopping at the containing table |
|
1684 for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) { |
|
1685 nsIAtom* frameType = rs->frame->GetType(); |
|
1686 NS_ASSERTION(IS_TABLE_CELL(frameType) || |
|
1687 nsGkAtoms::tableRowFrame == frameType || |
|
1688 nsGkAtoms::tableRowGroupFrame == frameType || |
|
1689 nsGkAtoms::tableFrame == frameType, |
|
1690 "unexpected frame type"); |
|
1691 |
|
1692 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
|
1693 if (nsGkAtoms::tableFrame == frameType) { |
|
1694 NS_ASSERTION(rs != &aReflowState, |
|
1695 "should not request special height reflow for table"); |
|
1696 // always stop when we reach a table |
|
1697 break; |
|
1698 } |
|
1699 } |
|
1700 } |
|
1701 |
|
1702 /****************************************************************************************** |
|
1703 * Before reflow, intrinsic width calculation is done using GetMinWidth |
|
1704 * and GetPrefWidth. This used to be known as pass 1 reflow. |
|
1705 * |
|
1706 * After the intrinsic width calculation, the table determines the |
|
1707 * column widths using BalanceColumnWidths() and |
|
1708 * then reflows each child again with a constrained avail width. This reflow is referred to |
|
1709 * as the pass 2 reflow. |
|
1710 * |
|
1711 * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow |
|
1712 * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed |
|
1713 * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables |
|
1714 * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is |
|
1715 * necessary because the table cannot split until after the pass 2 reflow. The mechanics of |
|
1716 * the special height reflow (variety a) are as follows: |
|
1717 * |
|
1718 * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow() |
|
1719 * to indicate that it should get the reflow. It does this when it has a percent height but |
|
1720 * no computed height by calling CheckRequestSpecialHeightReflow(). This method calls |
|
1721 * RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until |
|
1722 * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For |
|
1723 * percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight() |
|
1724 * is called (the cell is the reflow state's mPercentHeightObserver in this case). |
|
1725 * NotifyPercentHeight() calls RequestSpecialHeightReflow(). |
|
1726 * |
|
1727 * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it |
|
1728 * will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow |
|
1729 * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow() |
|
1730 * returns true because in that case another special height reflow will be coming along with the |
|
1731 * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when |
|
1732 * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then |
|
1733 * appropriate heights will not be known. |
|
1734 * |
|
1735 * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2 |
|
1736 * reflow, they return their last desired sizes during the special height reflow. The reflow only |
|
1737 * permits percent height frames inside the cells to resize based on the cells height and that height |
|
1738 * was determined during the pass 2 reflow. |
|
1739 * |
|
1740 * So, in the case of deeply nested tables, all of the tables that were told to initiate a special |
|
1741 * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow |
|
1742 * until the current initiator is its containing table. Since these reflows are only received by |
|
1743 * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal. |
|
1744 * |
|
1745 * The type of special reflow that occurs during printing (variety b) follows the same mechanism except |
|
1746 * that all frames will receive the reflow even if they don't really need them. |
|
1747 * |
|
1748 * Open issues with the special height reflow: |
|
1749 * |
|
1750 * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are |
|
1751 * really quite different. This would avoid unnecessary reflows during printing. |
|
1752 * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245). |
|
1753 * However, this can also occur if a cell has a fixed height and there is no special height reflow. |
|
1754 * |
|
1755 * XXXldb Special height reflow should really be its own method, not |
|
1756 * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on |
|
1757 * the contents of the cells to do the necessary vertical resizing. |
|
1758 * |
|
1759 ******************************************************************************************/ |
|
1760 |
|
1761 /* Layout the entire inner table. */ |
|
1762 nsresult nsTableFrame::Reflow(nsPresContext* aPresContext, |
|
1763 nsHTMLReflowMetrics& aDesiredSize, |
|
1764 const nsHTMLReflowState& aReflowState, |
|
1765 nsReflowStatus& aStatus) |
|
1766 { |
|
1767 DO_GLOBAL_REFLOW_COUNT("nsTableFrame"); |
|
1768 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); |
|
1769 bool isPaginated = aPresContext->IsPaginated(); |
|
1770 |
|
1771 aStatus = NS_FRAME_COMPLETE; |
|
1772 if (!GetPrevInFlow() && !mTableLayoutStrategy) { |
|
1773 NS_ASSERTION(false, "strategy should have been created in Init"); |
|
1774 return NS_ERROR_NULL_POINTER; |
|
1775 } |
|
1776 nsresult rv = NS_OK; |
|
1777 |
|
1778 // see if collapsing borders need to be calculated |
|
1779 if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) { |
|
1780 CalcBCBorders(); |
|
1781 } |
|
1782 |
|
1783 aDesiredSize.Width() = aReflowState.AvailableWidth(); |
|
1784 |
|
1785 // Check for an overflow list, and append any row group frames being pushed |
|
1786 MoveOverflowToChildList(); |
|
1787 |
|
1788 bool haveDesiredHeight = false; |
|
1789 SetHaveReflowedColGroups(false); |
|
1790 |
|
1791 // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a |
|
1792 // constrained initial reflow and other reflows which require either a strategy init or balance. |
|
1793 // This isn't done during an unconstrained reflow, because it will occur later when the parent |
|
1794 // reflows with a constrained width. |
|
1795 if (NS_SUBTREE_DIRTY(this) || |
|
1796 aReflowState.ShouldReflowAllKids() || |
|
1797 IsGeometryDirty() || |
|
1798 aReflowState.mFlags.mVResize) { |
|
1799 |
|
1800 if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE || |
|
1801 // Also check mVResize, to handle the first Reflow preceding a |
|
1802 // special height Reflow, when we've already had a special height |
|
1803 // Reflow (where mComputedHeight would not be |
|
1804 // NS_UNCONSTRAINEDSIZE, but without a style change in between). |
|
1805 aReflowState.mFlags.mVResize) { |
|
1806 // XXX Eventually, we should modify DistributeHeightToRows to use |
|
1807 // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height. |
|
1808 // That way, it will make its calculations based on internal table |
|
1809 // frame heights as they are before they ever had any extra height |
|
1810 // distributed to them. In the meantime, this reflows all the |
|
1811 // internal table frames, which restores them to their state before |
|
1812 // DistributeHeightToRows was called. |
|
1813 SetGeometryDirty(); |
|
1814 } |
|
1815 |
|
1816 bool needToInitiateSpecialReflow = |
|
1817 !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT); |
|
1818 // see if an extra reflow will be necessary in pagination mode when there is a specified table height |
|
1819 if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight())) { |
|
1820 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); |
|
1821 if ((tableSpecifiedHeight > 0) && |
|
1822 (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) { |
|
1823 needToInitiateSpecialReflow = true; |
|
1824 } |
|
1825 } |
|
1826 nsIFrame* lastChildReflowed = nullptr; |
|
1827 |
|
1828 NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow, |
|
1829 "Shouldn't be in special height reflow here!"); |
|
1830 |
|
1831 // do the pass 2 reflow unless this is a special height reflow and we will be |
|
1832 // initiating a special height reflow |
|
1833 // XXXldb I changed this. Should I change it back? |
|
1834 |
|
1835 // if we need to initiate a special height reflow, then don't constrain the |
|
1836 // height of the reflow before that |
|
1837 nscoord availHeight = needToInitiateSpecialReflow |
|
1838 ? NS_UNCONSTRAINEDSIZE : aReflowState.AvailableHeight(); |
|
1839 |
|
1840 ReflowTable(aDesiredSize, aReflowState, availHeight, |
|
1841 lastChildReflowed, aStatus); |
|
1842 |
|
1843 // reevaluate special height reflow conditions |
|
1844 if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) |
|
1845 needToInitiateSpecialReflow = true; |
|
1846 |
|
1847 // XXXldb Are all these conditions correct? |
|
1848 if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) { |
|
1849 // XXXldb Do we need to set the mVResize flag on any reflow states? |
|
1850 |
|
1851 nsHTMLReflowState &mutable_rs = |
|
1852 const_cast<nsHTMLReflowState&>(aReflowState); |
|
1853 |
|
1854 // distribute extra vertical space to rows |
|
1855 CalcDesiredHeight(aReflowState, aDesiredSize); |
|
1856 mutable_rs.mFlags.mSpecialHeightReflow = true; |
|
1857 |
|
1858 ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(), |
|
1859 lastChildReflowed, aStatus); |
|
1860 |
|
1861 if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) { |
|
1862 // if there is an incomplete child, then set the desired height to include it but not the next one |
|
1863 nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
|
1864 aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY() + |
|
1865 lastChildReflowed->GetRect().YMost(); |
|
1866 } |
|
1867 haveDesiredHeight = true; |
|
1868 |
|
1869 mutable_rs.mFlags.mSpecialHeightReflow = false; |
|
1870 } |
|
1871 } |
|
1872 else { |
|
1873 // Calculate the overflow area contribution from our children. |
|
1874 for (nsIFrame* kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) { |
|
1875 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kid); |
|
1876 } |
|
1877 } |
|
1878 |
|
1879 aDesiredSize.Width() = aReflowState.ComputedWidth() + |
|
1880 aReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
|
1881 if (!haveDesiredHeight) { |
|
1882 CalcDesiredHeight(aReflowState, aDesiredSize); |
|
1883 } |
|
1884 if (IsRowInserted()) { |
|
1885 ProcessRowInserted(aDesiredSize.Height()); |
|
1886 } |
|
1887 |
|
1888 nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
|
1889 SetColumnDimensions(aDesiredSize.Height(), borderPadding); |
|
1890 if (NeedToCollapse() && |
|
1891 (NS_UNCONSTRAINEDSIZE != aReflowState.AvailableWidth())) { |
|
1892 AdjustForCollapsingRowsCols(aDesiredSize, borderPadding); |
|
1893 } |
|
1894 |
|
1895 // If there are any relatively-positioned table parts, we need to reflow their |
|
1896 // absolutely-positioned descendants now that their dimensions are final. |
|
1897 FixupPositionedTableParts(aPresContext, aDesiredSize, aReflowState); |
|
1898 |
|
1899 // make sure the table overflow area does include the table rect. |
|
1900 nsRect tableRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height()) ; |
|
1901 |
|
1902 if (!ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) { |
|
1903 // collapsed border may leak out |
|
1904 nsMargin bcMargin = GetExcludedOuterBCBorder(); |
|
1905 tableRect.Inflate(bcMargin); |
|
1906 } |
|
1907 aDesiredSize.mOverflowAreas.UnionAllWith(tableRect); |
|
1908 |
|
1909 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) || |
|
1910 nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) { |
|
1911 nsIFrame::InvalidateFrame(); |
|
1912 } |
|
1913 |
|
1914 FinishAndStoreOverflow(&aDesiredSize); |
|
1915 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); |
|
1916 return rv; |
|
1917 } |
|
1918 |
|
1919 void |
|
1920 nsTableFrame::FixupPositionedTableParts(nsPresContext* aPresContext, |
|
1921 nsHTMLReflowMetrics& aDesiredSize, |
|
1922 const nsHTMLReflowState& aReflowState) |
|
1923 { |
|
1924 auto positionedParts = |
|
1925 static_cast<FrameTArray*>(Properties().Get(PositionedTablePartArray())); |
|
1926 if (!positionedParts) { |
|
1927 return; |
|
1928 } |
|
1929 |
|
1930 OverflowChangedTracker overflowTracker; |
|
1931 overflowTracker.SetSubtreeRoot(this); |
|
1932 |
|
1933 for (size_t i = 0; i < positionedParts->Length(); ++i) { |
|
1934 nsIFrame* positionedPart = positionedParts->ElementAt(i); |
|
1935 |
|
1936 // As we've already finished reflow, positionedParts's size and overflow |
|
1937 // areas have already been assigned, so we just pull them back out. |
|
1938 nsSize size(positionedPart->GetSize()); |
|
1939 nsHTMLReflowMetrics desiredSize(aReflowState.GetWritingMode()); |
|
1940 desiredSize.Width() = size.width; |
|
1941 desiredSize.Height() = size.height; |
|
1942 desiredSize.mOverflowAreas = positionedPart->GetOverflowAreasRelativeToSelf(); |
|
1943 |
|
1944 // Construct a dummy reflow state and reflow status. |
|
1945 // XXX(seth): Note that the dummy reflow state doesn't have a correct |
|
1946 // chain of parent reflow states. It also doesn't necessarily have a |
|
1947 // correct containing block. |
|
1948 nsHTMLReflowState reflowState(aPresContext, positionedPart, |
|
1949 aReflowState.rendContext, |
|
1950 nsSize(size.width, NS_UNCONSTRAINEDSIZE), |
|
1951 nsHTMLReflowState::DUMMY_PARENT_REFLOW_STATE); |
|
1952 nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; |
|
1953 |
|
1954 // Reflow absolutely-positioned descendants of the positioned part. |
|
1955 // FIXME: Unconditionally using NS_UNCONSTRAINEDSIZE for the height and |
|
1956 // ignoring any change to the reflow status aren't correct. We'll never |
|
1957 // paginate absolutely positioned frames. |
|
1958 overflowTracker.AddFrame(positionedPart, |
|
1959 OverflowChangedTracker::CHILDREN_AND_PARENT_CHANGED); |
|
1960 nsFrame* positionedFrame = static_cast<nsFrame*>(positionedPart); |
|
1961 positionedFrame->FinishReflowWithAbsoluteFrames(PresContext(), |
|
1962 desiredSize, |
|
1963 reflowState, |
|
1964 reflowStatus, |
|
1965 true); |
|
1966 } |
|
1967 |
|
1968 // Propagate updated overflow areas up the tree. |
|
1969 overflowTracker.Flush(); |
|
1970 |
|
1971 // Update our own overflow areas. (OverflowChangedTracker doesn't update the |
|
1972 // subtree root itself.) |
|
1973 aDesiredSize.SetOverflowAreasToDesiredBounds(); |
|
1974 nsLayoutUtils::UnionChildOverflow(this, aDesiredSize.mOverflowAreas); |
|
1975 } |
|
1976 |
|
1977 bool |
|
1978 nsTableFrame::UpdateOverflow() |
|
1979 { |
|
1980 nsRect bounds(nsPoint(0, 0), GetSize()); |
|
1981 |
|
1982 // As above in Reflow, make sure the table overflow area includes the table |
|
1983 // rect, and check for collapsed borders leaking out. |
|
1984 if (!ShouldApplyOverflowClipping(this, StyleDisplay())) { |
|
1985 nsMargin bcMargin = GetExcludedOuterBCBorder(); |
|
1986 bounds.Inflate(bcMargin); |
|
1987 } |
|
1988 |
|
1989 nsOverflowAreas overflowAreas(bounds, bounds); |
|
1990 nsLayoutUtils::UnionChildOverflow(this, overflowAreas); |
|
1991 |
|
1992 return FinishAndStoreOverflow(overflowAreas, GetSize()); |
|
1993 } |
|
1994 |
|
1995 nsresult |
|
1996 nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize, |
|
1997 const nsHTMLReflowState& aReflowState, |
|
1998 nscoord aAvailHeight, |
|
1999 nsIFrame*& aLastChildReflowed, |
|
2000 nsReflowStatus& aStatus) |
|
2001 { |
|
2002 nsresult rv = NS_OK; |
|
2003 aLastChildReflowed = nullptr; |
|
2004 |
|
2005 if (!GetPrevInFlow()) { |
|
2006 mTableLayoutStrategy->ComputeColumnWidths(aReflowState); |
|
2007 } |
|
2008 // Constrain our reflow width to the computed table width (of the 1st in flow). |
|
2009 // and our reflow height to our avail height minus border, padding, cellspacing |
|
2010 aDesiredSize.Width() = aReflowState.ComputedWidth() + |
|
2011 aReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
|
2012 nsTableReflowState reflowState(*PresContext(), aReflowState, *this, |
|
2013 aDesiredSize.Width(), aAvailHeight); |
|
2014 ReflowChildren(reflowState, aStatus, aLastChildReflowed, |
|
2015 aDesiredSize.mOverflowAreas); |
|
2016 |
|
2017 ReflowColGroups(aReflowState.rendContext); |
|
2018 return rv; |
|
2019 } |
|
2020 |
|
2021 nsIFrame* |
|
2022 nsTableFrame::GetFirstBodyRowGroupFrame() |
|
2023 { |
|
2024 nsIFrame* headerFrame = nullptr; |
|
2025 nsIFrame* footerFrame = nullptr; |
|
2026 |
|
2027 for (nsIFrame* kidFrame = mFrames.FirstChild(); nullptr != kidFrame; ) { |
|
2028 const nsStyleDisplay* childDisplay = kidFrame->StyleDisplay(); |
|
2029 |
|
2030 // We expect the header and footer row group frames to be first, and we only |
|
2031 // allow one header and one footer |
|
2032 if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) { |
|
2033 if (headerFrame) { |
|
2034 // We already have a header frame and so this header frame is treated |
|
2035 // like an ordinary body row group frame |
|
2036 return kidFrame; |
|
2037 } |
|
2038 headerFrame = kidFrame; |
|
2039 |
|
2040 } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) { |
|
2041 if (footerFrame) { |
|
2042 // We already have a footer frame and so this footer frame is treated |
|
2043 // like an ordinary body row group frame |
|
2044 return kidFrame; |
|
2045 } |
|
2046 footerFrame = kidFrame; |
|
2047 |
|
2048 } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) { |
|
2049 return kidFrame; |
|
2050 } |
|
2051 |
|
2052 // Get the next child |
|
2053 kidFrame = kidFrame->GetNextSibling(); |
|
2054 } |
|
2055 |
|
2056 return nullptr; |
|
2057 } |
|
2058 |
|
2059 // Table specific version that takes into account repeated header and footer |
|
2060 // frames when continuing table frames |
|
2061 void |
|
2062 nsTableFrame::PushChildren(const RowGroupArray& aRowGroups, |
|
2063 int32_t aPushFrom) |
|
2064 { |
|
2065 NS_PRECONDITION(aPushFrom > 0, "pushing first child"); |
|
2066 |
|
2067 // extract the frames from the array into a sibling list |
|
2068 nsFrameList frames; |
|
2069 uint32_t childX; |
|
2070 for (childX = aPushFrom; childX < aRowGroups.Length(); ++childX) { |
|
2071 nsTableRowGroupFrame* rgFrame = aRowGroups[childX]; |
|
2072 if (!rgFrame->IsRepeatable()) { |
|
2073 mFrames.RemoveFrame(rgFrame); |
|
2074 frames.AppendFrame(nullptr, rgFrame); |
|
2075 } |
|
2076 } |
|
2077 |
|
2078 if (frames.IsEmpty()) { |
|
2079 return; |
|
2080 } |
|
2081 |
|
2082 nsTableFrame* nextInFlow = static_cast<nsTableFrame*>(GetNextInFlow()); |
|
2083 if (nextInFlow) { |
|
2084 // Insert the frames after any repeated header and footer frames. |
|
2085 nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame(); |
|
2086 nsIFrame* prevSibling = nullptr; |
|
2087 if (firstBodyFrame) { |
|
2088 prevSibling = firstBodyFrame->GetPrevSibling(); |
|
2089 } |
|
2090 // When pushing and pulling frames we need to check for whether any |
|
2091 // views need to be reparented. |
|
2092 ReparentFrameViewList(frames, this, nextInFlow); |
|
2093 nextInFlow->mFrames.InsertFrames(nextInFlow, prevSibling, |
|
2094 frames); |
|
2095 } |
|
2096 else { |
|
2097 // Add the frames to our overflow list. |
|
2098 SetOverflowFrames(frames); |
|
2099 } |
|
2100 } |
|
2101 |
|
2102 // collapsing row groups, rows, col groups and cols are accounted for after both passes of |
|
2103 // reflow so that it has no effect on the calculations of reflow. |
|
2104 void |
|
2105 nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize, |
|
2106 nsMargin aBorderPadding) |
|
2107 { |
|
2108 nscoord yTotalOffset = 0; // total offset among all rows in all row groups |
|
2109 |
|
2110 // reset the bit, it will be set again if row/rowgroup or col/colgroup are |
|
2111 // collapsed |
|
2112 SetNeedToCollapse(false); |
|
2113 |
|
2114 // collapse the rows and/or row groups as necessary |
|
2115 // Get the ordered children |
|
2116 RowGroupArray rowGroups; |
|
2117 OrderRowGroups(rowGroups); |
|
2118 |
|
2119 nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow()); |
|
2120 nscoord width = firstInFlow->GetCollapsedWidth(aBorderPadding); |
|
2121 nscoord rgWidth = width - 2 * GetCellSpacingX(); |
|
2122 nsOverflowAreas overflow; |
|
2123 // Walk the list of children |
|
2124 for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) { |
|
2125 nsTableRowGroupFrame* rgFrame = rowGroups[childX]; |
|
2126 NS_ASSERTION(rgFrame, "Must have row group frame here"); |
|
2127 yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth); |
|
2128 ConsiderChildOverflow(overflow, rgFrame); |
|
2129 } |
|
2130 |
|
2131 aDesiredSize.Height() -= yTotalOffset; |
|
2132 aDesiredSize.Width() = width; |
|
2133 overflow.UnionAllWith(nsRect(0, 0, aDesiredSize.Width(), aDesiredSize.Height())); |
|
2134 FinishAndStoreOverflow(overflow, |
|
2135 nsSize(aDesiredSize.Width(), aDesiredSize.Height())); |
|
2136 } |
|
2137 |
|
2138 |
|
2139 nscoord |
|
2140 nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding) |
|
2141 { |
|
2142 NS_ASSERTION(!GetPrevInFlow(), "GetCollapsedWidth called on next in flow"); |
|
2143 nscoord cellSpacingX = GetCellSpacingX(); |
|
2144 nscoord width = cellSpacingX; |
|
2145 width += aBorderPadding.left + aBorderPadding.right; |
|
2146 for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame; |
|
2147 groupFrame = groupFrame->GetNextSibling()) { |
|
2148 const nsStyleVisibility* groupVis = groupFrame->StyleVisibility(); |
|
2149 bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible); |
|
2150 nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame; |
|
2151 for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame; |
|
2152 colFrame = colFrame->GetNextCol()) { |
|
2153 const nsStyleDisplay* colDisplay = colFrame->StyleDisplay(); |
|
2154 int32_t colX = colFrame->GetColIndex(); |
|
2155 if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) { |
|
2156 const nsStyleVisibility* colVis = colFrame->StyleVisibility(); |
|
2157 bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible); |
|
2158 int32_t colWidth = GetColumnWidth(colX); |
|
2159 if (!collapseGroup && !collapseCol) { |
|
2160 width += colWidth; |
|
2161 if (ColumnHasCellSpacingBefore(colX)) |
|
2162 width += cellSpacingX; |
|
2163 } |
|
2164 else { |
|
2165 SetNeedToCollapse(true); |
|
2166 } |
|
2167 } |
|
2168 } |
|
2169 } |
|
2170 return width; |
|
2171 } |
|
2172 |
|
2173 /* virtual */ void |
|
2174 nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) |
|
2175 { |
|
2176 nsContainerFrame::DidSetStyleContext(aOldStyleContext); |
|
2177 |
|
2178 if (!aOldStyleContext) //avoid this on init |
|
2179 return; |
|
2180 |
|
2181 if (IsBorderCollapse() && |
|
2182 BCRecalcNeeded(aOldStyleContext, StyleContext())) { |
|
2183 SetFullBCDamageArea(); |
|
2184 } |
|
2185 |
|
2186 //avoid this on init or nextinflow |
|
2187 if (!mTableLayoutStrategy || GetPrevInFlow()) |
|
2188 return; |
|
2189 |
|
2190 bool isAuto = IsAutoLayout(); |
|
2191 if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) { |
|
2192 nsITableLayoutStrategy* temp; |
|
2193 if (isAuto) |
|
2194 temp = new BasicTableLayoutStrategy(this); |
|
2195 else |
|
2196 temp = new FixedTableLayoutStrategy(this); |
|
2197 |
|
2198 if (temp) { |
|
2199 delete mTableLayoutStrategy; |
|
2200 mTableLayoutStrategy = temp; |
|
2201 } |
|
2202 } |
|
2203 } |
|
2204 |
|
2205 |
|
2206 |
|
2207 nsresult |
|
2208 nsTableFrame::AppendFrames(ChildListID aListID, |
|
2209 nsFrameList& aFrameList) |
|
2210 { |
|
2211 NS_ASSERTION(aListID == kPrincipalList || aListID == kColGroupList, |
|
2212 "unexpected child list"); |
|
2213 |
|
2214 // Because we actually have two child lists, one for col group frames and one |
|
2215 // for everything else, we need to look at each frame individually |
|
2216 // XXX The frame construction code should be separating out child frames |
|
2217 // based on the type, bug 343048. |
|
2218 while (!aFrameList.IsEmpty()) { |
|
2219 nsIFrame* f = aFrameList.FirstChild(); |
|
2220 aFrameList.RemoveFrame(f); |
|
2221 |
|
2222 // See what kind of frame we have |
|
2223 const nsStyleDisplay* display = f->StyleDisplay(); |
|
2224 |
|
2225 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) { |
|
2226 nsTableColGroupFrame* lastColGroup = |
|
2227 nsTableColGroupFrame::GetLastRealColGroup(this); |
|
2228 int32_t startColIndex = (lastColGroup) |
|
2229 ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0; |
|
2230 mColGroups.InsertFrame(nullptr, lastColGroup, f); |
|
2231 // Insert the colgroup and its cols into the table |
|
2232 InsertColGroups(startColIndex, |
|
2233 nsFrameList::Slice(mColGroups, f, f->GetNextSibling())); |
|
2234 } else if (IsRowGroup(display->mDisplay)) { |
|
2235 // Append the new row group frame to the sibling chain |
|
2236 mFrames.AppendFrame(nullptr, f); |
|
2237 |
|
2238 // insert the row group and its rows into the table |
|
2239 InsertRowGroups(nsFrameList::Slice(mFrames, f, nullptr)); |
|
2240 } else { |
|
2241 // Nothing special to do, just add the frame to our child list |
|
2242 NS_NOTREACHED("How did we get here? Frame construction screwed up"); |
|
2243 mFrames.AppendFrame(nullptr, f); |
|
2244 } |
|
2245 } |
|
2246 |
|
2247 #ifdef DEBUG_TABLE_CELLMAP |
|
2248 printf("=== TableFrame::AppendFrames\n"); |
|
2249 Dump(true, true, true); |
|
2250 #endif |
|
2251 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
|
2252 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
2253 SetGeometryDirty(); |
|
2254 |
|
2255 return NS_OK; |
|
2256 } |
|
2257 |
|
2258 // Needs to be at file scope or ArrayLength fails to compile. |
|
2259 struct ChildListInsertions { |
|
2260 nsIFrame::ChildListID mID; |
|
2261 nsFrameList mList; |
|
2262 }; |
|
2263 |
|
2264 nsresult |
|
2265 nsTableFrame::InsertFrames(ChildListID aListID, |
|
2266 nsIFrame* aPrevFrame, |
|
2267 nsFrameList& aFrameList) |
|
2268 { |
|
2269 // The frames in aFrameList can be a mix of row group frames and col group |
|
2270 // frames. The problem is that they should go in separate child lists so |
|
2271 // we need to deal with that here... |
|
2272 // XXX The frame construction code should be separating out child frames |
|
2273 // based on the type, bug 343048. |
|
2274 |
|
2275 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, |
|
2276 "inserting after sibling frame with different parent"); |
|
2277 |
|
2278 if ((aPrevFrame && !aPrevFrame->GetNextSibling()) || |
|
2279 (!aPrevFrame && GetChildList(aListID).IsEmpty())) { |
|
2280 // Treat this like an append; still a workaround for bug 343048. |
|
2281 return AppendFrames(aListID, aFrameList); |
|
2282 } |
|
2283 |
|
2284 // Collect ColGroupFrames into a separate list and insert those separately |
|
2285 // from the other frames (bug 759249). |
|
2286 ChildListInsertions insertions[2]; // ColGroup, other |
|
2287 const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay(); |
|
2288 nsFrameList::FrameLinkEnumerator e(aFrameList); |
|
2289 for (; !aFrameList.IsEmpty(); e.Next()) { |
|
2290 nsIFrame* next = e.NextFrame(); |
|
2291 if (!next || next->StyleDisplay()->mDisplay != display->mDisplay) { |
|
2292 nsFrameList head = aFrameList.ExtractHead(e); |
|
2293 if (display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) { |
|
2294 insertions[0].mID = kColGroupList; |
|
2295 insertions[0].mList.AppendFrames(nullptr, head); |
|
2296 } else { |
|
2297 insertions[1].mID = kPrincipalList; |
|
2298 insertions[1].mList.AppendFrames(nullptr, head); |
|
2299 } |
|
2300 if (!next) { |
|
2301 break; |
|
2302 } |
|
2303 display = next->StyleDisplay(); |
|
2304 } |
|
2305 } |
|
2306 for (uint32_t i = 0; i < ArrayLength(insertions); ++i) { |
|
2307 // We pass aPrevFrame for both ColGroup and other frames since |
|
2308 // HomogenousInsertFrames will only use it if it's a suitable |
|
2309 // prev-sibling for the frames in the frame list. |
|
2310 if (!insertions[i].mList.IsEmpty()) { |
|
2311 HomogenousInsertFrames(insertions[i].mID, aPrevFrame, |
|
2312 insertions[i].mList); |
|
2313 } |
|
2314 } |
|
2315 return NS_OK; |
|
2316 } |
|
2317 |
|
2318 void |
|
2319 nsTableFrame::HomogenousInsertFrames(ChildListID aListID, |
|
2320 nsIFrame* aPrevFrame, |
|
2321 nsFrameList& aFrameList) |
|
2322 { |
|
2323 // See what kind of frame we have |
|
2324 const nsStyleDisplay* display = aFrameList.FirstChild()->StyleDisplay(); |
|
2325 #ifdef DEBUG |
|
2326 // Verify that either all siblings have display:table-column-group, or they |
|
2327 // all have display values different from table-column-group. |
|
2328 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { |
|
2329 const nsStyleDisplay* nextDisplay = e.get()->StyleDisplay(); |
|
2330 MOZ_ASSERT((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) == |
|
2331 (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP), |
|
2332 "heterogenous childlist"); |
|
2333 } |
|
2334 #endif |
|
2335 if (aPrevFrame) { |
|
2336 const nsStyleDisplay* prevDisplay = aPrevFrame->StyleDisplay(); |
|
2337 // Make sure they belong on the same frame list |
|
2338 if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) != |
|
2339 (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) { |
|
2340 // the previous frame is not valid, see comment at ::AppendFrames |
|
2341 // XXXbz Using content indices here means XBL will get screwed |
|
2342 // over... Oh, well. |
|
2343 nsIFrame* pseudoFrame = aFrameList.FirstChild(); |
|
2344 nsIContent* parentContent = GetContent(); |
|
2345 nsIContent* content; |
|
2346 aPrevFrame = nullptr; |
|
2347 while (pseudoFrame && (parentContent == |
|
2348 (content = pseudoFrame->GetContent()))) { |
|
2349 pseudoFrame = pseudoFrame->GetFirstPrincipalChild(); |
|
2350 } |
|
2351 nsCOMPtr<nsIContent> container = content->GetParent(); |
|
2352 if (MOZ_LIKELY(container)) { // XXX need this null-check, see bug 411823. |
|
2353 int32_t newIndex = container->IndexOf(content); |
|
2354 nsIFrame* kidFrame; |
|
2355 bool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == |
|
2356 display->mDisplay); |
|
2357 nsTableColGroupFrame* lastColGroup; |
|
2358 if (isColGroup) { |
|
2359 kidFrame = mColGroups.FirstChild(); |
|
2360 lastColGroup = nsTableColGroupFrame::GetLastRealColGroup(this); |
|
2361 } |
|
2362 else { |
|
2363 kidFrame = mFrames.FirstChild(); |
|
2364 } |
|
2365 // Important: need to start at a value smaller than all valid indices |
|
2366 int32_t lastIndex = -1; |
|
2367 while (kidFrame) { |
|
2368 if (isColGroup) { |
|
2369 if (kidFrame == lastColGroup) { |
|
2370 aPrevFrame = kidFrame; // there is no real colgroup after this one |
|
2371 break; |
|
2372 } |
|
2373 } |
|
2374 pseudoFrame = kidFrame; |
|
2375 while (pseudoFrame && (parentContent == |
|
2376 (content = pseudoFrame->GetContent()))) { |
|
2377 pseudoFrame = pseudoFrame->GetFirstPrincipalChild(); |
|
2378 } |
|
2379 int32_t index = container->IndexOf(content); |
|
2380 if (index > lastIndex && index < newIndex) { |
|
2381 lastIndex = index; |
|
2382 aPrevFrame = kidFrame; |
|
2383 } |
|
2384 kidFrame = kidFrame->GetNextSibling(); |
|
2385 } |
|
2386 } |
|
2387 } |
|
2388 } |
|
2389 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) { |
|
2390 NS_ASSERTION(aListID == kColGroupList, "unexpected child list"); |
|
2391 // Insert the column group frames |
|
2392 const nsFrameList::Slice& newColgroups = |
|
2393 mColGroups.InsertFrames(nullptr, aPrevFrame, aFrameList); |
|
2394 // find the starting col index for the first new col group |
|
2395 int32_t startColIndex = 0; |
|
2396 if (aPrevFrame) { |
|
2397 nsTableColGroupFrame* prevColGroup = |
|
2398 (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame, |
|
2399 nsGkAtoms::tableColGroupFrame); |
|
2400 if (prevColGroup) { |
|
2401 startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount(); |
|
2402 } |
|
2403 } |
|
2404 InsertColGroups(startColIndex, newColgroups); |
|
2405 } else if (IsRowGroup(display->mDisplay)) { |
|
2406 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
|
2407 // Insert the frames in the sibling chain |
|
2408 const nsFrameList::Slice& newRowGroups = |
|
2409 mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); |
|
2410 |
|
2411 InsertRowGroups(newRowGroups); |
|
2412 } else { |
|
2413 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
|
2414 NS_NOTREACHED("How did we even get here?"); |
|
2415 // Just insert the frame and don't worry about reflowing it |
|
2416 mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList); |
|
2417 return; |
|
2418 } |
|
2419 |
|
2420 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange, |
|
2421 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
2422 SetGeometryDirty(); |
|
2423 #ifdef DEBUG_TABLE_CELLMAP |
|
2424 printf("=== TableFrame::InsertFrames\n"); |
|
2425 Dump(true, true, true); |
|
2426 #endif |
|
2427 return; |
|
2428 } |
|
2429 |
|
2430 void |
|
2431 nsTableFrame::DoRemoveFrame(ChildListID aListID, |
|
2432 nsIFrame* aOldFrame) |
|
2433 { |
|
2434 if (aListID == kColGroupList) { |
|
2435 nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling(); |
|
2436 nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame; |
|
2437 int32_t firstColIndex = colGroup->GetStartColumnIndex(); |
|
2438 int32_t lastColIndex = firstColIndex + colGroup->GetColCount() - 1; |
|
2439 mColGroups.DestroyFrame(aOldFrame); |
|
2440 nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex); |
|
2441 // remove the cols from the table |
|
2442 int32_t colX; |
|
2443 for (colX = lastColIndex; colX >= firstColIndex; colX--) { |
|
2444 nsTableColFrame* colFrame = mColFrames.SafeElementAt(colX); |
|
2445 if (colFrame) { |
|
2446 RemoveCol(colGroup, colX, true, false); |
|
2447 } |
|
2448 } |
|
2449 |
|
2450 int32_t numAnonymousColsToAdd = GetColCount() - mColFrames.Length(); |
|
2451 if (numAnonymousColsToAdd > 0) { |
|
2452 // this sets the child list, updates the col cache and cell map |
|
2453 AppendAnonymousColFrames(numAnonymousColsToAdd); |
|
2454 } |
|
2455 |
|
2456 } else { |
|
2457 NS_ASSERTION(aListID == kPrincipalList, "unexpected child list"); |
|
2458 nsTableRowGroupFrame* rgFrame = |
|
2459 static_cast<nsTableRowGroupFrame*>(aOldFrame); |
|
2460 // remove the row group from the cell map |
|
2461 nsTableCellMap* cellMap = GetCellMap(); |
|
2462 if (cellMap) { |
|
2463 cellMap->RemoveGroupCellMap(rgFrame); |
|
2464 } |
|
2465 |
|
2466 // remove the row group frame from the sibling chain |
|
2467 mFrames.DestroyFrame(aOldFrame); |
|
2468 |
|
2469 // the removal of a row group changes the cellmap, the columns might change |
|
2470 if (cellMap) { |
|
2471 cellMap->Synchronize(this); |
|
2472 // Create an empty slice |
|
2473 ResetRowIndices(nsFrameList::Slice(mFrames, nullptr, nullptr)); |
|
2474 nsIntRect damageArea; |
|
2475 cellMap->RebuildConsideringCells(nullptr, nullptr, 0, 0, false, damageArea); |
|
2476 |
|
2477 static_cast<nsTableFrame*>(FirstInFlow())->MatchCellMapToColCache(cellMap); |
|
2478 } |
|
2479 } |
|
2480 } |
|
2481 |
|
2482 nsresult |
|
2483 nsTableFrame::RemoveFrame(ChildListID aListID, |
|
2484 nsIFrame* aOldFrame) |
|
2485 { |
|
2486 NS_ASSERTION(aListID == kColGroupList || |
|
2487 NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP != |
|
2488 aOldFrame->StyleDisplay()->mDisplay, |
|
2489 "Wrong list name; use kColGroupList iff colgroup"); |
|
2490 nsIPresShell* shell = PresContext()->PresShell(); |
|
2491 nsTableFrame* lastParent = nullptr; |
|
2492 while (aOldFrame) { |
|
2493 nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation(); |
|
2494 nsTableFrame* parent = static_cast<nsTableFrame*>(aOldFrame->GetParent()); |
|
2495 if (parent != lastParent) { |
|
2496 parent->DrainSelfOverflowList(); |
|
2497 } |
|
2498 parent->DoRemoveFrame(aListID, aOldFrame); |
|
2499 aOldFrame = oldFrameNextContinuation; |
|
2500 if (parent != lastParent) { |
|
2501 // for now, just bail and recalc all of the collapsing borders |
|
2502 // as the cellmap changes we need to recalc |
|
2503 if (parent->IsBorderCollapse()) { |
|
2504 parent->SetFullBCDamageArea(); |
|
2505 } |
|
2506 parent->SetGeometryDirty(); |
|
2507 shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange, |
|
2508 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
2509 lastParent = parent; |
|
2510 } |
|
2511 } |
|
2512 #ifdef DEBUG_TABLE_CELLMAP |
|
2513 printf("=== TableFrame::RemoveFrame\n"); |
|
2514 Dump(true, true, true); |
|
2515 #endif |
|
2516 return NS_OK; |
|
2517 } |
|
2518 |
|
2519 /* virtual */ nsMargin |
|
2520 nsTableFrame::GetUsedBorder() const |
|
2521 { |
|
2522 if (!IsBorderCollapse()) |
|
2523 return nsContainerFrame::GetUsedBorder(); |
|
2524 |
|
2525 return GetIncludedOuterBCBorder(); |
|
2526 } |
|
2527 |
|
2528 /* virtual */ nsMargin |
|
2529 nsTableFrame::GetUsedPadding() const |
|
2530 { |
|
2531 if (!IsBorderCollapse()) |
|
2532 return nsContainerFrame::GetUsedPadding(); |
|
2533 |
|
2534 return nsMargin(0,0,0,0); |
|
2535 } |
|
2536 |
|
2537 /* virtual */ nsMargin |
|
2538 nsTableFrame::GetUsedMargin() const |
|
2539 { |
|
2540 // The margin is inherited to the outer table frame via |
|
2541 // the ::-moz-table-outer rule in ua.css. |
|
2542 return nsMargin(0, 0, 0, 0); |
|
2543 } |
|
2544 |
|
2545 // Destructor function for BCPropertyData properties |
|
2546 static void |
|
2547 DestroyBCProperty(void* aPropertyValue) |
|
2548 { |
|
2549 delete static_cast<BCPropertyData*>(aPropertyValue); |
|
2550 } |
|
2551 |
|
2552 NS_DECLARE_FRAME_PROPERTY(TableBCProperty, DestroyBCProperty) |
|
2553 |
|
2554 BCPropertyData* |
|
2555 nsTableFrame::GetBCProperty(bool aCreateIfNecessary) const |
|
2556 { |
|
2557 FrameProperties props = Properties(); |
|
2558 BCPropertyData* value = static_cast<BCPropertyData*> |
|
2559 (props.Get(TableBCProperty())); |
|
2560 if (!value && aCreateIfNecessary) { |
|
2561 value = new BCPropertyData(); |
|
2562 props.Set(TableBCProperty(), value); |
|
2563 } |
|
2564 |
|
2565 return value; |
|
2566 } |
|
2567 |
|
2568 static void |
|
2569 DivideBCBorderSize(BCPixelSize aPixelSize, |
|
2570 BCPixelSize& aSmallHalf, |
|
2571 BCPixelSize& aLargeHalf) |
|
2572 { |
|
2573 aSmallHalf = aPixelSize / 2; |
|
2574 aLargeHalf = aPixelSize - aSmallHalf; |
|
2575 } |
|
2576 |
|
2577 nsMargin |
|
2578 nsTableFrame::GetOuterBCBorder() const |
|
2579 { |
|
2580 if (NeedToCalcBCBorders()) |
|
2581 const_cast<nsTableFrame*>(this)->CalcBCBorders(); |
|
2582 |
|
2583 nsMargin border(0, 0, 0, 0); |
|
2584 int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); |
|
2585 BCPropertyData* propData = GetBCProperty(); |
|
2586 if (propData) { |
|
2587 border.top = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth); |
|
2588 border.right = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth); |
|
2589 border.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth); |
|
2590 border.left = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth); |
|
2591 } |
|
2592 return border; |
|
2593 } |
|
2594 |
|
2595 nsMargin |
|
2596 nsTableFrame::GetIncludedOuterBCBorder() const |
|
2597 { |
|
2598 if (NeedToCalcBCBorders()) |
|
2599 const_cast<nsTableFrame*>(this)->CalcBCBorders(); |
|
2600 |
|
2601 nsMargin border(0, 0, 0, 0); |
|
2602 int32_t p2t = nsPresContext::AppUnitsPerCSSPixel(); |
|
2603 BCPropertyData* propData = GetBCProperty(); |
|
2604 if (propData) { |
|
2605 border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth); |
|
2606 border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightCellBorderWidth); |
|
2607 border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth); |
|
2608 border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftCellBorderWidth); |
|
2609 } |
|
2610 return border; |
|
2611 } |
|
2612 |
|
2613 nsMargin |
|
2614 nsTableFrame::GetExcludedOuterBCBorder() const |
|
2615 { |
|
2616 return GetOuterBCBorder() - GetIncludedOuterBCBorder(); |
|
2617 } |
|
2618 |
|
2619 static |
|
2620 void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState, |
|
2621 nsStyleContext& aStyleContext, |
|
2622 nsMargin& aBorderPadding) |
|
2623 { |
|
2624 // XXXbz Either we _do_ have a reflow state and then we can use its |
|
2625 // mComputedBorderPadding or we don't and then we get the padding |
|
2626 // wrong! |
|
2627 const nsStyleBorder* border = aStyleContext.StyleBorder(); |
|
2628 aBorderPadding = border->GetComputedBorder(); |
|
2629 if (aReflowState) { |
|
2630 aBorderPadding += aReflowState->ComputedPhysicalPadding(); |
|
2631 } |
|
2632 } |
|
2633 |
|
2634 nsMargin |
|
2635 nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const |
|
2636 { |
|
2637 nsMargin offset(0,0,0,0); |
|
2638 if (IsBorderCollapse()) { |
|
2639 offset = GetIncludedOuterBCBorder(); |
|
2640 } |
|
2641 else { |
|
2642 GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset); |
|
2643 } |
|
2644 return offset; |
|
2645 } |
|
2646 |
|
2647 void |
|
2648 nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState) |
|
2649 { |
|
2650 nsMargin collapseBorder; |
|
2651 nsMargin padding(0,0,0,0); |
|
2652 nsMargin* pCollapseBorder = nullptr; |
|
2653 nsPresContext* presContext = PresContext(); |
|
2654 if (IsBorderCollapse()) { |
|
2655 nsTableRowGroupFrame* rgFrame = |
|
2656 static_cast<nsTableRowGroupFrame*>(aReflowState.frame); |
|
2657 pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder); |
|
2658 } |
|
2659 aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding); |
|
2660 |
|
2661 NS_ASSERTION(!mBits.mResizedColumns || |
|
2662 !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow, |
|
2663 "should not resize columns on special height reflow"); |
|
2664 if (mBits.mResizedColumns) { |
|
2665 aReflowState.mFlags.mHResize = true; |
|
2666 } |
|
2667 } |
|
2668 |
|
2669 // Position and size aKidFrame and update our reflow state. The origin of |
|
2670 // aKidRect is relative to the upper-left origin of our frame |
|
2671 void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState, |
|
2672 nsIFrame* aKidFrame, |
|
2673 nsHTMLReflowMetrics& aKidDesiredSize, |
|
2674 const nsRect& aOriginalKidRect, |
|
2675 const nsRect& aOriginalKidVisualOverflow) |
|
2676 { |
|
2677 bool isFirstReflow = |
|
2678 (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0; |
|
2679 |
|
2680 // Place and size the child |
|
2681 FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr, |
|
2682 aReflowState.x, aReflowState.y, 0); |
|
2683 |
|
2684 InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow, |
|
2685 isFirstReflow); |
|
2686 |
|
2687 // Adjust the running y-offset |
|
2688 aReflowState.y += aKidDesiredSize.Height(); |
|
2689 |
|
2690 // If our height is constrained, then update the available height |
|
2691 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
|
2692 aReflowState.availSize.height -= aKidDesiredSize.Height(); |
|
2693 } |
|
2694 } |
|
2695 |
|
2696 void |
|
2697 nsTableFrame::OrderRowGroups(RowGroupArray& aChildren, |
|
2698 nsTableRowGroupFrame** aHead, |
|
2699 nsTableRowGroupFrame** aFoot) const |
|
2700 { |
|
2701 aChildren.Clear(); |
|
2702 nsTableRowGroupFrame* head = nullptr; |
|
2703 nsTableRowGroupFrame* foot = nullptr; |
|
2704 |
|
2705 nsIFrame* kidFrame = mFrames.FirstChild(); |
|
2706 while (kidFrame) { |
|
2707 const nsStyleDisplay* kidDisplay = kidFrame->StyleDisplay(); |
|
2708 nsTableRowGroupFrame* rowGroup = |
|
2709 static_cast<nsTableRowGroupFrame*>(kidFrame); |
|
2710 |
|
2711 switch (kidDisplay->mDisplay) { |
|
2712 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP: |
|
2713 if (head) { // treat additional thead like tbody |
|
2714 aChildren.AppendElement(rowGroup); |
|
2715 } |
|
2716 else { |
|
2717 head = rowGroup; |
|
2718 } |
|
2719 break; |
|
2720 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP: |
|
2721 if (foot) { // treat additional tfoot like tbody |
|
2722 aChildren.AppendElement(rowGroup); |
|
2723 } |
|
2724 else { |
|
2725 foot = rowGroup; |
|
2726 } |
|
2727 break; |
|
2728 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP: |
|
2729 aChildren.AppendElement(rowGroup); |
|
2730 break; |
|
2731 default: |
|
2732 NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?"); |
|
2733 // Just ignore it |
|
2734 break; |
|
2735 } |
|
2736 // Get the next sibling but skip it if it's also the next-in-flow, since |
|
2737 // a next-in-flow will not be part of the current table. |
|
2738 while (kidFrame) { |
|
2739 nsIFrame* nif = kidFrame->GetNextInFlow(); |
|
2740 kidFrame = kidFrame->GetNextSibling(); |
|
2741 if (kidFrame != nif) |
|
2742 break; |
|
2743 } |
|
2744 } |
|
2745 |
|
2746 // put the thead first |
|
2747 if (head) { |
|
2748 aChildren.InsertElementAt(0, head); |
|
2749 } |
|
2750 if (aHead) |
|
2751 *aHead = head; |
|
2752 // put the tfoot after the last tbody |
|
2753 if (foot) { |
|
2754 aChildren.AppendElement(foot); |
|
2755 } |
|
2756 if (aFoot) |
|
2757 *aFoot = foot; |
|
2758 } |
|
2759 |
|
2760 nsTableRowGroupFrame* |
|
2761 nsTableFrame::GetTHead() const |
|
2762 { |
|
2763 nsIFrame* kidFrame = mFrames.FirstChild(); |
|
2764 while (kidFrame) { |
|
2765 if (kidFrame->StyleDisplay()->mDisplay == |
|
2766 NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) { |
|
2767 return static_cast<nsTableRowGroupFrame*>(kidFrame); |
|
2768 } |
|
2769 |
|
2770 // Get the next sibling but skip it if it's also the next-in-flow, since |
|
2771 // a next-in-flow will not be part of the current table. |
|
2772 while (kidFrame) { |
|
2773 nsIFrame* nif = kidFrame->GetNextInFlow(); |
|
2774 kidFrame = kidFrame->GetNextSibling(); |
|
2775 if (kidFrame != nif) |
|
2776 break; |
|
2777 } |
|
2778 } |
|
2779 |
|
2780 return nullptr; |
|
2781 } |
|
2782 |
|
2783 nsTableRowGroupFrame* |
|
2784 nsTableFrame::GetTFoot() const |
|
2785 { |
|
2786 nsIFrame* kidFrame = mFrames.FirstChild(); |
|
2787 while (kidFrame) { |
|
2788 if (kidFrame->StyleDisplay()->mDisplay == |
|
2789 NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) { |
|
2790 return static_cast<nsTableRowGroupFrame*>(kidFrame); |
|
2791 } |
|
2792 |
|
2793 // Get the next sibling but skip it if it's also the next-in-flow, since |
|
2794 // a next-in-flow will not be part of the current table. |
|
2795 while (kidFrame) { |
|
2796 nsIFrame* nif = kidFrame->GetNextInFlow(); |
|
2797 kidFrame = kidFrame->GetNextSibling(); |
|
2798 if (kidFrame != nif) |
|
2799 break; |
|
2800 } |
|
2801 } |
|
2802 |
|
2803 return nullptr; |
|
2804 } |
|
2805 |
|
2806 static bool |
|
2807 IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight) |
|
2808 { |
|
2809 return aFrameHeight < (aPageHeight / 4); |
|
2810 } |
|
2811 |
|
2812 nsresult |
|
2813 nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState, |
|
2814 nsTableRowGroupFrame* aFrame, |
|
2815 nscoord* aDesiredHeight) |
|
2816 { |
|
2817 nsPresContext* presContext = PresContext(); |
|
2818 nscoord pageHeight = presContext->GetPageSize().height; |
|
2819 |
|
2820 // Reflow the child with unconstrainted height |
|
2821 nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, |
|
2822 aFrame, |
|
2823 nsSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE), |
|
2824 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT); |
|
2825 InitChildReflowState(kidReflowState); |
|
2826 kidReflowState.mFlags.mIsTopOfPage = true; |
|
2827 nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); |
|
2828 desiredSize.Width() = desiredSize.Height() = 0; |
|
2829 nsReflowStatus status; |
|
2830 nsresult rv = ReflowChild(aFrame, presContext, desiredSize, kidReflowState, |
|
2831 aReflowState.x, aReflowState.y, 0, status); |
|
2832 NS_ENSURE_SUCCESS(rv, rv); |
|
2833 // The child will be reflowed again "for real" so no need to place it now |
|
2834 |
|
2835 aFrame->SetRepeatable(IsRepeatable(desiredSize.Height(), pageHeight)); |
|
2836 *aDesiredHeight = desiredSize.Height(); |
|
2837 return NS_OK; |
|
2838 } |
|
2839 |
|
2840 void |
|
2841 nsTableFrame::PlaceRepeatedFooter(nsTableReflowState& aReflowState, |
|
2842 nsTableRowGroupFrame *aTfoot, |
|
2843 nscoord aFooterHeight) |
|
2844 { |
|
2845 nsPresContext* presContext = PresContext(); |
|
2846 nsSize kidAvailSize(aReflowState.availSize); |
|
2847 kidAvailSize.height = aFooterHeight; |
|
2848 nsHTMLReflowState footerReflowState(presContext, |
|
2849 aReflowState.reflowState, |
|
2850 aTfoot, kidAvailSize, |
|
2851 -1, -1, |
|
2852 nsHTMLReflowState::CALLER_WILL_INIT); |
|
2853 InitChildReflowState(footerReflowState); |
|
2854 aReflowState.y += GetCellSpacingY(); |
|
2855 |
|
2856 nsRect origTfootRect = aTfoot->GetRect(); |
|
2857 nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect(); |
|
2858 |
|
2859 nsReflowStatus footerStatus; |
|
2860 nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); |
|
2861 desiredSize.Width() = desiredSize.Height() = 0; |
|
2862 ReflowChild(aTfoot, presContext, desiredSize, footerReflowState, |
|
2863 aReflowState.x, aReflowState.y, 0, footerStatus); |
|
2864 PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect, |
|
2865 origTfootVisualOverflow); |
|
2866 } |
|
2867 |
|
2868 // Reflow the children based on the avail size and reason in aReflowState |
|
2869 // update aReflowMetrics a aStatus |
|
2870 nsresult |
|
2871 nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState, |
|
2872 nsReflowStatus& aStatus, |
|
2873 nsIFrame*& aLastChildReflowed, |
|
2874 nsOverflowAreas& aOverflowAreas) |
|
2875 { |
|
2876 aStatus = NS_FRAME_COMPLETE; |
|
2877 aLastChildReflowed = nullptr; |
|
2878 |
|
2879 nsIFrame* prevKidFrame = nullptr; |
|
2880 nsresult rv = NS_OK; |
|
2881 nscoord cellSpacingY = GetCellSpacingY(); |
|
2882 |
|
2883 nsPresContext* presContext = PresContext(); |
|
2884 // XXXldb Should we be checking constrained height instead? |
|
2885 // tables are not able to pull back children from its next inflow, so even |
|
2886 // under paginated contexts tables are should not paginate if they are inside |
|
2887 // column set |
|
2888 bool isPaginated = presContext->IsPaginated() && |
|
2889 NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height && |
|
2890 aReflowState.reflowState.mFlags.mTableIsSplittable; |
|
2891 |
|
2892 aOverflowAreas.Clear(); |
|
2893 |
|
2894 bool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() || |
|
2895 mBits.mResizedColumns || |
|
2896 IsGeometryDirty(); |
|
2897 |
|
2898 RowGroupArray rowGroups; |
|
2899 nsTableRowGroupFrame *thead, *tfoot; |
|
2900 OrderRowGroups(rowGroups, &thead, &tfoot); |
|
2901 bool pageBreak = false; |
|
2902 nscoord footerHeight = 0; |
|
2903 |
|
2904 // Determine the repeatablility of headers and footers, and also the desired |
|
2905 // height of any repeatable footer. |
|
2906 // The repeatability of headers on continued tables is handled |
|
2907 // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame. |
|
2908 // We handle the repeatability of footers again here because we need to |
|
2909 // determine the footer's height anyway. We could perhaps optimize by |
|
2910 // using the footer's prev-in-flow's height instead of reflowing it again, |
|
2911 // but there's no real need. |
|
2912 if (isPaginated) { |
|
2913 if (thead && !GetPrevInFlow()) { |
|
2914 nscoord desiredHeight; |
|
2915 rv = SetupHeaderFooterChild(aReflowState, thead, &desiredHeight); |
|
2916 if (NS_FAILED(rv)) |
|
2917 return rv; |
|
2918 } |
|
2919 if (tfoot) { |
|
2920 rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight); |
|
2921 if (NS_FAILED(rv)) |
|
2922 return rv; |
|
2923 } |
|
2924 } |
|
2925 // if the child is a tbody in paginated mode reduce the height by a repeated footer |
|
2926 bool allowRepeatedFooter = false; |
|
2927 for (uint32_t childX = 0; childX < rowGroups.Length(); childX++) { |
|
2928 nsIFrame* kidFrame = rowGroups[childX]; |
|
2929 // Get the frame state bits |
|
2930 // See if we should only reflow the dirty child frames |
|
2931 if (reflowAllKids || |
|
2932 NS_SUBTREE_DIRTY(kidFrame) || |
|
2933 (aReflowState.reflowState.mFlags.mSpecialHeightReflow && |
|
2934 (isPaginated || (kidFrame->GetStateBits() & |
|
2935 NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) { |
|
2936 if (pageBreak) { |
|
2937 if (allowRepeatedFooter) { |
|
2938 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
|
2939 } |
|
2940 else if (tfoot && tfoot->IsRepeatable()) { |
|
2941 tfoot->SetRepeatable(false); |
|
2942 } |
|
2943 PushChildren(rowGroups, childX); |
|
2944 aStatus = NS_FRAME_NOT_COMPLETE; |
|
2945 break; |
|
2946 } |
|
2947 |
|
2948 nsSize kidAvailSize(aReflowState.availSize); |
|
2949 allowRepeatedFooter = false; |
|
2950 if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) { |
|
2951 nsTableRowGroupFrame* kidRG = |
|
2952 static_cast<nsTableRowGroupFrame*>(kidFrame); |
|
2953 if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) { |
|
2954 // the child is a tbody and there is a repeatable footer |
|
2955 NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!"); |
|
2956 if (footerHeight + cellSpacingY < kidAvailSize.height) { |
|
2957 allowRepeatedFooter = true; |
|
2958 kidAvailSize.height -= footerHeight + cellSpacingY; |
|
2959 } |
|
2960 } |
|
2961 } |
|
2962 |
|
2963 nsRect oldKidRect = kidFrame->GetRect(); |
|
2964 nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect(); |
|
2965 |
|
2966 nsHTMLReflowMetrics desiredSize(aReflowState.reflowState); |
|
2967 desiredSize.Width() = desiredSize.Height() = 0; |
|
2968 |
|
2969 // Reflow the child into the available space |
|
2970 nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, |
|
2971 kidFrame, kidAvailSize, |
|
2972 -1, -1, |
|
2973 nsHTMLReflowState::CALLER_WILL_INIT); |
|
2974 InitChildReflowState(kidReflowState); |
|
2975 |
|
2976 // If this isn't the first row group, and the previous row group has a |
|
2977 // nonzero YMost, then we can't be at the top of the page. |
|
2978 // We ignore a repeated head row group in this check to avoid causing |
|
2979 // infinite loops in some circumstances - see bug 344883. |
|
2980 if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) && |
|
2981 (rowGroups[childX - 1]->GetRect().YMost() > 0)) { |
|
2982 kidReflowState.mFlags.mIsTopOfPage = false; |
|
2983 } |
|
2984 aReflowState.y += cellSpacingY; |
|
2985 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
|
2986 aReflowState.availSize.height -= cellSpacingY; |
|
2987 } |
|
2988 // record the presence of a next in flow, it might get destroyed so we |
|
2989 // need to reorder the row group array |
|
2990 bool reorder = false; |
|
2991 if (kidFrame->GetNextInFlow()) |
|
2992 reorder = true; |
|
2993 |
|
2994 rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState, |
|
2995 aReflowState.x, aReflowState.y, 0, aStatus); |
|
2996 |
|
2997 if (reorder) { |
|
2998 // reorder row groups the reflow may have changed the nextinflows |
|
2999 OrderRowGroups(rowGroups, &thead, &tfoot); |
|
3000 childX = rowGroups.IndexOf(kidFrame); |
|
3001 if (childX == RowGroupArray::NoIndex) { |
|
3002 // XXXbz can this happen? |
|
3003 childX = rowGroups.Length(); |
|
3004 } |
|
3005 } |
|
3006 if (isPaginated && !NS_FRAME_IS_FULLY_COMPLETE(aStatus) && |
|
3007 ShouldAvoidBreakInside(aReflowState.reflowState)) { |
|
3008 aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
|
3009 break; |
|
3010 } |
|
3011 // see if the rowgroup did not fit on this page might be pushed on |
|
3012 // the next page |
|
3013 if (isPaginated && |
|
3014 (NS_INLINE_IS_BREAK_BEFORE(aStatus) || |
|
3015 (NS_FRAME_IS_COMPLETE(aStatus) && |
|
3016 (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight()) && |
|
3017 kidReflowState.AvailableHeight() < desiredSize.Height()))) { |
|
3018 if (ShouldAvoidBreakInside(aReflowState.reflowState)) { |
|
3019 aStatus = NS_INLINE_LINE_BREAK_BEFORE(); |
|
3020 break; |
|
3021 } |
|
3022 // if we are on top of the page place with dataloss |
|
3023 if (kidReflowState.mFlags.mIsTopOfPage) { |
|
3024 if (childX+1 < rowGroups.Length()) { |
|
3025 nsIFrame* nextRowGroupFrame = rowGroups[childX + 1]; |
|
3026 if (nextRowGroupFrame) { |
|
3027 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, |
|
3028 oldKidVisualOverflow); |
|
3029 if (allowRepeatedFooter) { |
|
3030 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
|
3031 } |
|
3032 else if (tfoot && tfoot->IsRepeatable()) { |
|
3033 tfoot->SetRepeatable(false); |
|
3034 } |
|
3035 aStatus = NS_FRAME_NOT_COMPLETE; |
|
3036 PushChildren(rowGroups, childX + 1); |
|
3037 aLastChildReflowed = kidFrame; |
|
3038 break; |
|
3039 } |
|
3040 } |
|
3041 } |
|
3042 else { // we are not on top, push this rowgroup onto the next page |
|
3043 if (prevKidFrame) { // we had a rowgroup before so push this |
|
3044 if (allowRepeatedFooter) { |
|
3045 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
|
3046 } |
|
3047 else if (tfoot && tfoot->IsRepeatable()) { |
|
3048 tfoot->SetRepeatable(false); |
|
3049 } |
|
3050 aStatus = NS_FRAME_NOT_COMPLETE; |
|
3051 PushChildren(rowGroups, childX); |
|
3052 aLastChildReflowed = prevKidFrame; |
|
3053 break; |
|
3054 } |
|
3055 else { // we can't push so lets make clear how much space we need |
|
3056 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, |
|
3057 oldKidVisualOverflow); |
|
3058 aLastChildReflowed = kidFrame; |
|
3059 if (allowRepeatedFooter) { |
|
3060 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
|
3061 aLastChildReflowed = tfoot; |
|
3062 } |
|
3063 break; |
|
3064 } |
|
3065 } |
|
3066 } |
|
3067 |
|
3068 aLastChildReflowed = kidFrame; |
|
3069 |
|
3070 pageBreak = false; |
|
3071 // see if there is a page break after this row group or before the next one |
|
3072 if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated && |
|
3073 (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight())) { |
|
3074 nsIFrame* nextKid = |
|
3075 (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr; |
|
3076 pageBreak = PageBreakAfter(kidFrame, nextKid); |
|
3077 } |
|
3078 |
|
3079 // Place the child |
|
3080 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect, |
|
3081 oldKidVisualOverflow); |
|
3082 |
|
3083 // Remember where we just were in case we end up pushing children |
|
3084 prevKidFrame = kidFrame; |
|
3085 |
|
3086 // Special handling for incomplete children |
|
3087 if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) { |
|
3088 nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow(); |
|
3089 if (!kidNextInFlow) { |
|
3090 // The child doesn't have a next-in-flow so create a continuing |
|
3091 // frame. This hooks the child into the flow |
|
3092 kidNextInFlow = presContext->PresShell()->FrameConstructor()-> |
|
3093 CreateContinuingFrame(presContext, kidFrame, this); |
|
3094 |
|
3095 // Insert the kid's new next-in-flow into our sibling list... |
|
3096 mFrames.InsertFrame(nullptr, kidFrame, kidNextInFlow); |
|
3097 // and in rowGroups after childX so that it will get pushed below. |
|
3098 rowGroups.InsertElementAt(childX + 1, |
|
3099 static_cast<nsTableRowGroupFrame*>(kidNextInFlow)); |
|
3100 } else if (kidNextInFlow == kidFrame->GetNextSibling()) { |
|
3101 // OrderRowGroups excludes NIFs in the child list from 'rowGroups' |
|
3102 // so we deal with that here to make sure they get pushed. |
|
3103 MOZ_ASSERT(!rowGroups.Contains(kidNextInFlow), |
|
3104 "OrderRowGroups must not put our NIF in 'rowGroups'"); |
|
3105 rowGroups.InsertElementAt(childX + 1, |
|
3106 static_cast<nsTableRowGroupFrame*>(kidNextInFlow)); |
|
3107 } |
|
3108 |
|
3109 // We've used up all of our available space so push the remaining |
|
3110 // children. |
|
3111 if (allowRepeatedFooter) { |
|
3112 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight); |
|
3113 } |
|
3114 else if (tfoot && tfoot->IsRepeatable()) { |
|
3115 tfoot->SetRepeatable(false); |
|
3116 } |
|
3117 |
|
3118 nsIFrame* nextSibling = kidFrame->GetNextSibling(); |
|
3119 if (nextSibling) { |
|
3120 PushChildren(rowGroups, childX + 1); |
|
3121 } |
|
3122 break; |
|
3123 } |
|
3124 } |
|
3125 else { // it isn't being reflowed |
|
3126 aReflowState.y += cellSpacingY; |
|
3127 nsRect kidRect = kidFrame->GetRect(); |
|
3128 if (kidRect.y != aReflowState.y) { |
|
3129 // invalidate the old position |
|
3130 kidFrame->InvalidateFrameSubtree(); |
|
3131 kidRect.y = aReflowState.y; |
|
3132 kidFrame->SetRect(kidRect); // move to the new position |
|
3133 RePositionViews(kidFrame); |
|
3134 // invalidate the new position |
|
3135 kidFrame->InvalidateFrameSubtree(); |
|
3136 } |
|
3137 aReflowState.y += kidRect.height; |
|
3138 |
|
3139 // If our height is constrained then update the available height. |
|
3140 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) { |
|
3141 aReflowState.availSize.height -= cellSpacingY + kidRect.height; |
|
3142 } |
|
3143 } |
|
3144 ConsiderChildOverflow(aOverflowAreas, kidFrame); |
|
3145 } |
|
3146 |
|
3147 // We've now propagated the column resizes and geometry changes to all |
|
3148 // the children. |
|
3149 mBits.mResizedColumns = false; |
|
3150 ClearGeometryDirty(); |
|
3151 |
|
3152 return rv; |
|
3153 } |
|
3154 |
|
3155 void |
|
3156 nsTableFrame::ReflowColGroups(nsRenderingContext *aRenderingContext) |
|
3157 { |
|
3158 if (!GetPrevInFlow() && !HaveReflowedColGroups()) { |
|
3159 nsHTMLReflowMetrics kidMet(GetWritingMode()); |
|
3160 nsPresContext *presContext = PresContext(); |
|
3161 for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame; |
|
3162 kidFrame = kidFrame->GetNextSibling()) { |
|
3163 if (NS_SUBTREE_DIRTY(kidFrame)) { |
|
3164 // The column groups don't care about dimensions or reflow states. |
|
3165 nsHTMLReflowState kidReflowState(presContext, kidFrame, |
|
3166 aRenderingContext, nsSize(0,0)); |
|
3167 nsReflowStatus cgStatus; |
|
3168 ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0, |
|
3169 cgStatus); |
|
3170 FinishReflowChild(kidFrame, presContext, kidMet, nullptr, 0, 0, 0); |
|
3171 } |
|
3172 } |
|
3173 SetHaveReflowedColGroups(true); |
|
3174 } |
|
3175 } |
|
3176 |
|
3177 void |
|
3178 nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize) |
|
3179 { |
|
3180 nsTableCellMap* cellMap = GetCellMap(); |
|
3181 if (!cellMap) { |
|
3182 NS_ASSERTION(false, "never ever call me until the cell map is built!"); |
|
3183 aDesiredSize.Height() = 0; |
|
3184 return; |
|
3185 } |
|
3186 nscoord cellSpacingY = GetCellSpacingY(); |
|
3187 nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
|
3188 |
|
3189 // get the natural height based on the last child's (row group) rect |
|
3190 RowGroupArray rowGroups; |
|
3191 OrderRowGroups(rowGroups); |
|
3192 if (rowGroups.IsEmpty()) { |
|
3193 // tables can be used as rectangular items without content |
|
3194 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); |
|
3195 if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) && |
|
3196 (tableSpecifiedHeight > 0) && |
|
3197 eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) { |
|
3198 // empty tables should not have a size in quirks mode |
|
3199 aDesiredSize.Height() = tableSpecifiedHeight; |
|
3200 } |
|
3201 else |
|
3202 aDesiredSize.Height() = 0; |
|
3203 return; |
|
3204 } |
|
3205 int32_t rowCount = cellMap->GetRowCount(); |
|
3206 int32_t colCount = cellMap->GetColCount(); |
|
3207 nscoord desiredHeight = borderPadding.top + borderPadding.bottom; |
|
3208 if (rowCount > 0 && colCount > 0) { |
|
3209 desiredHeight += cellSpacingY; |
|
3210 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
3211 desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY; |
|
3212 } |
|
3213 } |
|
3214 |
|
3215 // see if a specified table height requires dividing additional space to rows |
|
3216 if (!GetPrevInFlow()) { |
|
3217 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState); |
|
3218 if ((tableSpecifiedHeight > 0) && |
|
3219 (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) && |
|
3220 (tableSpecifiedHeight > desiredHeight)) { |
|
3221 // proportionately distribute the excess height to unconstrained rows in each |
|
3222 // unconstrained row group. |
|
3223 DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight); |
|
3224 // this might have changed the overflow area incorporate the childframe overflow area. |
|
3225 for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) { |
|
3226 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame); |
|
3227 } |
|
3228 desiredHeight = tableSpecifiedHeight; |
|
3229 } |
|
3230 } |
|
3231 aDesiredSize.Height() = desiredHeight; |
|
3232 } |
|
3233 |
|
3234 static |
|
3235 void ResizeCells(nsTableFrame& aTableFrame) |
|
3236 { |
|
3237 nsTableFrame::RowGroupArray rowGroups; |
|
3238 aTableFrame.OrderRowGroups(rowGroups); |
|
3239 nsHTMLReflowMetrics tableDesiredSize(aTableFrame.GetWritingMode()); // ??? |
|
3240 nsRect tableRect = aTableFrame.GetRect(); |
|
3241 tableDesiredSize.Width() = tableRect.width; |
|
3242 tableDesiredSize.Height() = tableRect.height; |
|
3243 tableDesiredSize.SetOverflowAreasToDesiredBounds(); |
|
3244 |
|
3245 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
3246 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
3247 |
|
3248 nsRect rowGroupRect = rgFrame->GetRect(); |
|
3249 nsHTMLReflowMetrics groupDesiredSize(tableDesiredSize.GetWritingMode()); |
|
3250 groupDesiredSize.Width() = rowGroupRect.width; |
|
3251 groupDesiredSize.Height() = rowGroupRect.height; |
|
3252 groupDesiredSize.SetOverflowAreasToDesiredBounds(); |
|
3253 |
|
3254 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
|
3255 while (rowFrame) { |
|
3256 rowFrame->DidResize(); |
|
3257 rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowAreas, rowFrame); |
|
3258 rowFrame = rowFrame->GetNextRow(); |
|
3259 } |
|
3260 rgFrame->FinishAndStoreOverflow(&groupDesiredSize); |
|
3261 tableDesiredSize.mOverflowAreas.UnionWith(groupDesiredSize.mOverflowAreas + |
|
3262 rgFrame->GetPosition()); |
|
3263 } |
|
3264 aTableFrame.FinishAndStoreOverflow(&tableDesiredSize); |
|
3265 } |
|
3266 |
|
3267 void |
|
3268 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState, |
|
3269 nscoord aAmount) |
|
3270 { |
|
3271 nscoord cellSpacingY = GetCellSpacingY(); |
|
3272 |
|
3273 nsMargin borderPadding = GetChildAreaOffset(&aReflowState); |
|
3274 |
|
3275 RowGroupArray rowGroups; |
|
3276 OrderRowGroups(rowGroups); |
|
3277 |
|
3278 nscoord amountUsed = 0; |
|
3279 // distribute space to each pct height row whose row group doesn't have a computed |
|
3280 // height, and base the pct on the table height. If the row group had a computed |
|
3281 // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights |
|
3282 nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1)); |
|
3283 nscoord yOriginRG = borderPadding.top + GetCellSpacingY(); |
|
3284 nscoord yEndRG = yOriginRG; |
|
3285 uint32_t rgX; |
|
3286 for (rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
3287 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
3288 nscoord amountUsedByRG = 0; |
|
3289 nscoord yOriginRow = 0; |
|
3290 nsRect rgRect = rgFrame->GetRect(); |
|
3291 if (!rgFrame->HasStyleHeight()) { |
|
3292 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
|
3293 while (rowFrame) { |
|
3294 nsRect rowRect = rowFrame->GetRect(); |
|
3295 if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) { |
|
3296 nscoord pctHeight = rowFrame->GetHeight(pctBasis); |
|
3297 nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height); |
|
3298 if (amountForRow > 0) { |
|
3299 nsRect oldRowRect = rowRect; |
|
3300 rowRect.height += amountForRow; |
|
3301 // XXXbz we don't need to change rowRect.y to be yOriginRow? |
|
3302 rowFrame->SetRect(rowRect); |
|
3303 yOriginRow += rowRect.height + cellSpacingY; |
|
3304 yEndRG += rowRect.height + cellSpacingY; |
|
3305 amountUsed += amountForRow; |
|
3306 amountUsedByRG += amountForRow; |
|
3307 //rowFrame->DidResize(); |
|
3308 nsTableFrame::RePositionViews(rowFrame); |
|
3309 |
|
3310 rgFrame->InvalidateFrameWithRect(oldRowRect); |
|
3311 rgFrame->InvalidateFrame(); |
|
3312 } |
|
3313 } |
|
3314 else { |
|
3315 if (amountUsed > 0 && yOriginRow != rowRect.y && |
|
3316 !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
|
3317 rowFrame->InvalidateFrameSubtree(); |
|
3318 rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); |
|
3319 nsTableFrame::RePositionViews(rowFrame); |
|
3320 rowFrame->InvalidateFrameSubtree(); |
|
3321 } |
|
3322 yOriginRow += rowRect.height + cellSpacingY; |
|
3323 yEndRG += rowRect.height + cellSpacingY; |
|
3324 } |
|
3325 rowFrame = rowFrame->GetNextRow(); |
|
3326 } |
|
3327 if (amountUsed > 0) { |
|
3328 if (rgRect.y != yOriginRG) { |
|
3329 rgFrame->InvalidateFrameSubtree(); |
|
3330 } |
|
3331 |
|
3332 nsRect origRgRect = rgRect; |
|
3333 nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect(); |
|
3334 |
|
3335 rgRect.y = yOriginRG; |
|
3336 rgRect.height += amountUsedByRG; |
|
3337 |
|
3338 rgFrame->SetRect(rgRect); |
|
3339 |
|
3340 nsTableFrame::InvalidateTableFrame(rgFrame, origRgRect, |
|
3341 origRgVisualOverflow, false); |
|
3342 } |
|
3343 } |
|
3344 else if (amountUsed > 0 && yOriginRG != rgRect.y) { |
|
3345 rgFrame->InvalidateFrameSubtree(); |
|
3346 rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG)); |
|
3347 // Make sure child views are properly positioned |
|
3348 nsTableFrame::RePositionViews(rgFrame); |
|
3349 rgFrame->InvalidateFrameSubtree(); |
|
3350 } |
|
3351 yOriginRG = yEndRG; |
|
3352 } |
|
3353 |
|
3354 if (amountUsed >= aAmount) { |
|
3355 ResizeCells(*this); |
|
3356 return; |
|
3357 } |
|
3358 |
|
3359 // get the first row without a style height where its row group has an |
|
3360 // unconstrained height |
|
3361 nsTableRowGroupFrame* firstUnStyledRG = nullptr; |
|
3362 nsTableRowFrame* firstUnStyledRow = nullptr; |
|
3363 for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) { |
|
3364 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
3365 if (!rgFrame->HasStyleHeight()) { |
|
3366 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
|
3367 while (rowFrame) { |
|
3368 if (!rowFrame->HasStyleHeight()) { |
|
3369 firstUnStyledRG = rgFrame; |
|
3370 firstUnStyledRow = rowFrame; |
|
3371 break; |
|
3372 } |
|
3373 rowFrame = rowFrame->GetNextRow(); |
|
3374 } |
|
3375 } |
|
3376 } |
|
3377 |
|
3378 nsTableRowFrame* lastEligibleRow = nullptr; |
|
3379 // Accumulate the correct divisor. This will be the total total height of all |
|
3380 // unstyled rows inside unstyled row groups, unless there are none, in which |
|
3381 // case, it will be number of all rows. If the unstyled rows don't have a |
|
3382 // height, divide the space equally among them. |
|
3383 nscoord divisor = 0; |
|
3384 int32_t eligibleRows = 0; |
|
3385 bool expandEmptyRows = false; |
|
3386 |
|
3387 if (!firstUnStyledRow) { |
|
3388 // there is no unstyled row |
|
3389 divisor = GetRowCount(); |
|
3390 } |
|
3391 else { |
|
3392 for (rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
3393 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
3394 if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) { |
|
3395 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
|
3396 while (rowFrame) { |
|
3397 if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) { |
|
3398 NS_ASSERTION(rowFrame->GetSize().height >= 0, |
|
3399 "negative row frame height"); |
|
3400 divisor += rowFrame->GetSize().height; |
|
3401 eligibleRows++; |
|
3402 lastEligibleRow = rowFrame; |
|
3403 } |
|
3404 rowFrame = rowFrame->GetNextRow(); |
|
3405 } |
|
3406 } |
|
3407 } |
|
3408 if (divisor <= 0) { |
|
3409 if (eligibleRows > 0) { |
|
3410 expandEmptyRows = true; |
|
3411 } |
|
3412 else { |
|
3413 NS_ERROR("invalid divisor"); |
|
3414 return; |
|
3415 } |
|
3416 } |
|
3417 } |
|
3418 // allocate the extra height to the unstyled row groups and rows |
|
3419 nscoord heightToDistribute = aAmount - amountUsed; |
|
3420 yOriginRG = borderPadding.top + cellSpacingY; |
|
3421 yEndRG = yOriginRG; |
|
3422 for (rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
3423 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
3424 nscoord amountUsedByRG = 0; |
|
3425 nscoord yOriginRow = 0; |
|
3426 nsRect rgRect = rgFrame->GetRect(); |
|
3427 nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect(); |
|
3428 // see if there is an eligible row group or we distribute to all rows |
|
3429 if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) { |
|
3430 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); |
|
3431 while (rowFrame) { |
|
3432 nsRect rowRect = rowFrame->GetRect(); |
|
3433 nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect(); |
|
3434 // see if there is an eligible row or we distribute to all rows |
|
3435 if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) { |
|
3436 float ratio; |
|
3437 if (eligibleRows) { |
|
3438 if (!expandEmptyRows) { |
|
3439 // The amount of additional space each row gets is proportional to |
|
3440 // its height |
|
3441 ratio = float(rowRect.height) / float(divisor); |
|
3442 } else { |
|
3443 // empty rows get all the same additional space |
|
3444 ratio = 1.0f / float(eligibleRows); |
|
3445 } |
|
3446 } |
|
3447 else { |
|
3448 // all rows get the same additional space |
|
3449 ratio = 1.0f / float(divisor); |
|
3450 } |
|
3451 // give rows their additional space, except for the last row which |
|
3452 // gets the remainder |
|
3453 nscoord amountForRow = (rowFrame == lastEligibleRow) |
|
3454 ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio); |
|
3455 amountForRow = std::min(amountForRow, aAmount - amountUsed); |
|
3456 |
|
3457 if (yOriginRow != rowRect.y) { |
|
3458 rowFrame->InvalidateFrameSubtree(); |
|
3459 } |
|
3460 |
|
3461 // update the row height |
|
3462 nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, |
|
3463 rowRect.height + amountForRow); |
|
3464 rowFrame->SetRect(newRowRect); |
|
3465 |
|
3466 yOriginRow += newRowRect.height + cellSpacingY; |
|
3467 yEndRG += newRowRect.height + cellSpacingY; |
|
3468 |
|
3469 amountUsed += amountForRow; |
|
3470 amountUsedByRG += amountForRow; |
|
3471 NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation"); |
|
3472 //rowFrame->DidResize(); |
|
3473 nsTableFrame::RePositionViews(rowFrame); |
|
3474 |
|
3475 nsTableFrame::InvalidateTableFrame(rowFrame, rowRect, rowVisualOverflow, |
|
3476 false); |
|
3477 } |
|
3478 else { |
|
3479 if (amountUsed > 0 && yOriginRow != rowRect.y) { |
|
3480 rowFrame->InvalidateFrameSubtree(); |
|
3481 rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow)); |
|
3482 nsTableFrame::RePositionViews(rowFrame); |
|
3483 rowFrame->InvalidateFrameSubtree(); |
|
3484 } |
|
3485 yOriginRow += rowRect.height + cellSpacingY; |
|
3486 yEndRG += rowRect.height + cellSpacingY; |
|
3487 } |
|
3488 rowFrame = rowFrame->GetNextRow(); |
|
3489 } |
|
3490 if (amountUsed > 0) { |
|
3491 if (rgRect.y != yOriginRG) { |
|
3492 rgFrame->InvalidateFrameSubtree(); |
|
3493 } |
|
3494 |
|
3495 rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width, |
|
3496 rgRect.height + amountUsedByRG)); |
|
3497 |
|
3498 nsTableFrame::InvalidateTableFrame(rgFrame, rgRect, rgVisualOverflow, |
|
3499 false); |
|
3500 } |
|
3501 // Make sure child views are properly positioned |
|
3502 } |
|
3503 else if (amountUsed > 0 && yOriginRG != rgRect.y) { |
|
3504 rgFrame->InvalidateFrameSubtree(); |
|
3505 rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG)); |
|
3506 // Make sure child views are properly positioned |
|
3507 nsTableFrame::RePositionViews(rgFrame); |
|
3508 rgFrame->InvalidateFrameSubtree(); |
|
3509 } |
|
3510 yOriginRG = yEndRG; |
|
3511 } |
|
3512 |
|
3513 ResizeCells(*this); |
|
3514 } |
|
3515 |
|
3516 int32_t nsTableFrame::GetColumnWidth(int32_t aColIndex) |
|
3517 { |
|
3518 nsTableFrame* firstInFlow = static_cast<nsTableFrame*>(FirstInFlow()); |
|
3519 if (this == firstInFlow) { |
|
3520 nsTableColFrame* colFrame = GetColFrame(aColIndex); |
|
3521 return colFrame ? colFrame->GetFinalWidth() : 0; |
|
3522 } |
|
3523 return firstInFlow->GetColumnWidth(aColIndex); |
|
3524 } |
|
3525 |
|
3526 // XXX: could cache this. But be sure to check style changes if you do! |
|
3527 nscoord nsTableFrame::GetCellSpacingX() |
|
3528 { |
|
3529 if (IsBorderCollapse()) |
|
3530 return 0; |
|
3531 |
|
3532 return StyleTableBorder()->mBorderSpacingX; |
|
3533 } |
|
3534 |
|
3535 // XXX: could cache this. But be sure to check style changes if you do! |
|
3536 nscoord nsTableFrame::GetCellSpacingY() |
|
3537 { |
|
3538 if (IsBorderCollapse()) |
|
3539 return 0; |
|
3540 |
|
3541 return StyleTableBorder()->mBorderSpacingY; |
|
3542 } |
|
3543 |
|
3544 |
|
3545 /* virtual */ nscoord |
|
3546 nsTableFrame::GetBaseline() const |
|
3547 { |
|
3548 nscoord ascent = 0; |
|
3549 RowGroupArray orderedRowGroups; |
|
3550 OrderRowGroups(orderedRowGroups); |
|
3551 nsTableRowFrame* firstRow = nullptr; |
|
3552 for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) { |
|
3553 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex]; |
|
3554 if (rgFrame->GetRowCount()) { |
|
3555 firstRow = rgFrame->GetFirstRow(); |
|
3556 ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline(); |
|
3557 break; |
|
3558 } |
|
3559 } |
|
3560 if (!firstRow) |
|
3561 ascent = GetRect().height; |
|
3562 return ascent; |
|
3563 } |
|
3564 /* ----- global methods ----- */ |
|
3565 |
|
3566 nsIFrame* |
|
3567 NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
3568 { |
|
3569 return new (aPresShell) nsTableFrame(aContext); |
|
3570 } |
|
3571 |
|
3572 NS_IMPL_FRAMEARENA_HELPERS(nsTableFrame) |
|
3573 |
|
3574 nsTableFrame* |
|
3575 nsTableFrame::GetTableFrame(nsIFrame* aFrame) |
|
3576 { |
|
3577 for (nsIFrame* ancestor = aFrame->GetParent(); ancestor; |
|
3578 ancestor = ancestor->GetParent()) { |
|
3579 if (nsGkAtoms::tableFrame == ancestor->GetType()) { |
|
3580 return static_cast<nsTableFrame*>(ancestor); |
|
3581 } |
|
3582 } |
|
3583 NS_RUNTIMEABORT("unable to find table parent"); |
|
3584 return nullptr; |
|
3585 } |
|
3586 |
|
3587 nsTableFrame* |
|
3588 nsTableFrame::GetTableFramePassingThrough(nsIFrame* aMustPassThrough, |
|
3589 nsIFrame* aFrame) |
|
3590 { |
|
3591 MOZ_ASSERT(aMustPassThrough == aFrame || |
|
3592 nsLayoutUtils::IsProperAncestorFrame(aMustPassThrough, aFrame), |
|
3593 "aMustPassThrough should be an ancestor"); |
|
3594 |
|
3595 // Retrieve the table frame, and ensure that we hit aMustPassThrough on the |
|
3596 // way. If we don't, just return null. |
|
3597 bool hitPassThroughFrame = false; |
|
3598 nsTableFrame* tableFrame = nullptr; |
|
3599 for (nsIFrame* ancestor = aFrame; ancestor; ancestor = ancestor->GetParent()) { |
|
3600 if (ancestor == aMustPassThrough) { |
|
3601 hitPassThroughFrame = true; |
|
3602 } |
|
3603 if (nsGkAtoms::tableFrame == ancestor->GetType()) { |
|
3604 tableFrame = static_cast<nsTableFrame*>(ancestor); |
|
3605 break; |
|
3606 } |
|
3607 } |
|
3608 |
|
3609 MOZ_ASSERT(tableFrame, "Should have a table frame here"); |
|
3610 return hitPassThroughFrame ? tableFrame : nullptr; |
|
3611 } |
|
3612 |
|
3613 bool |
|
3614 nsTableFrame::IsAutoHeight() |
|
3615 { |
|
3616 const nsStyleCoord &height = StylePosition()->mHeight; |
|
3617 // Don't consider calc() here like this quirk for percent. |
|
3618 return height.GetUnit() == eStyleUnit_Auto || |
|
3619 (height.GetUnit() == eStyleUnit_Percent && |
|
3620 height.GetPercentValue() <= 0.0f); |
|
3621 } |
|
3622 |
|
3623 nscoord |
|
3624 nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState) |
|
3625 { |
|
3626 nscoord height = aState.ComputedHeight(); |
|
3627 if (NS_AUTOHEIGHT != height) { |
|
3628 nsMargin borderPadding = GetChildAreaOffset(&aState); |
|
3629 height += borderPadding.top + borderPadding.bottom; |
|
3630 } |
|
3631 height = std::max(0, height); |
|
3632 |
|
3633 return height; |
|
3634 } |
|
3635 |
|
3636 bool |
|
3637 nsTableFrame::IsAutoLayout() |
|
3638 { |
|
3639 if (StyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO) |
|
3640 return true; |
|
3641 // a fixed-layout inline-table must have a width |
|
3642 // and tables with 'width: -moz-max-content' must be auto-layout |
|
3643 // (at least as long as FixedTableLayoutStrategy::GetPrefWidth returns |
|
3644 // nscoord_MAX) |
|
3645 const nsStyleCoord &width = StylePosition()->mWidth; |
|
3646 return (width.GetUnit() == eStyleUnit_Auto) || |
|
3647 (width.GetUnit() == eStyleUnit_Enumerated && |
|
3648 width.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT); |
|
3649 } |
|
3650 |
|
3651 #ifdef DEBUG_FRAME_DUMP |
|
3652 nsresult |
|
3653 nsTableFrame::GetFrameName(nsAString& aResult) const |
|
3654 { |
|
3655 return MakeFrameName(NS_LITERAL_STRING("Table"), aResult); |
|
3656 } |
|
3657 #endif |
|
3658 |
|
3659 // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that |
|
3660 // is of type aChildType |
|
3661 nsIFrame* |
|
3662 nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame, |
|
3663 nsIFrame* aPriorChildFrame, |
|
3664 nsIAtom* aChildType) |
|
3665 { |
|
3666 nsIFrame* result = nullptr; |
|
3667 if (!aPriorChildFrame) { |
|
3668 return result; |
|
3669 } |
|
3670 if (aChildType == aPriorChildFrame->GetType()) { |
|
3671 return aPriorChildFrame; |
|
3672 } |
|
3673 |
|
3674 // aPriorChildFrame is not of type aChildType, so we need start from |
|
3675 // the beginnng and find the closest one |
|
3676 nsIFrame* lastMatchingFrame = nullptr; |
|
3677 nsIFrame* childFrame = aParentFrame->GetFirstPrincipalChild(); |
|
3678 while (childFrame && (childFrame != aPriorChildFrame)) { |
|
3679 if (aChildType == childFrame->GetType()) { |
|
3680 lastMatchingFrame = childFrame; |
|
3681 } |
|
3682 childFrame = childFrame->GetNextSibling(); |
|
3683 } |
|
3684 return lastMatchingFrame; |
|
3685 } |
|
3686 |
|
3687 #ifdef DEBUG |
|
3688 void |
|
3689 nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame) |
|
3690 { |
|
3691 if (!aKidFrame) |
|
3692 return; |
|
3693 |
|
3694 nsIFrame* cFrame = aKidFrame->GetFirstPrincipalChild(); |
|
3695 while (cFrame) { |
|
3696 nsTableRowFrame *rowFrame = do_QueryFrame(cFrame); |
|
3697 if (rowFrame) { |
|
3698 printf("row(%d)=%p ", rowFrame->GetRowIndex(), |
|
3699 static_cast<void*>(rowFrame)); |
|
3700 nsIFrame* childFrame = cFrame->GetFirstPrincipalChild(); |
|
3701 while (childFrame) { |
|
3702 nsTableCellFrame *cellFrame = do_QueryFrame(childFrame); |
|
3703 if (cellFrame) { |
|
3704 int32_t colIndex; |
|
3705 cellFrame->GetColIndex(colIndex); |
|
3706 printf("cell(%d)=%p ", colIndex, static_cast<void*>(childFrame)); |
|
3707 } |
|
3708 childFrame = childFrame->GetNextSibling(); |
|
3709 } |
|
3710 printf("\n"); |
|
3711 } |
|
3712 else { |
|
3713 DumpRowGroup(rowFrame); |
|
3714 } |
|
3715 cFrame = cFrame->GetNextSibling(); |
|
3716 } |
|
3717 } |
|
3718 |
|
3719 void |
|
3720 nsTableFrame::Dump(bool aDumpRows, |
|
3721 bool aDumpCols, |
|
3722 bool aDumpCellMap) |
|
3723 { |
|
3724 printf("***START TABLE DUMP*** \n"); |
|
3725 // dump the columns widths array |
|
3726 printf("mColWidths="); |
|
3727 int32_t numCols = GetColCount(); |
|
3728 int32_t colX; |
|
3729 for (colX = 0; colX < numCols; colX++) { |
|
3730 printf("%d ", GetColumnWidth(colX)); |
|
3731 } |
|
3732 printf("\n"); |
|
3733 |
|
3734 if (aDumpRows) { |
|
3735 nsIFrame* kidFrame = mFrames.FirstChild(); |
|
3736 while (kidFrame) { |
|
3737 DumpRowGroup(kidFrame); |
|
3738 kidFrame = kidFrame->GetNextSibling(); |
|
3739 } |
|
3740 } |
|
3741 |
|
3742 if (aDumpCols) { |
|
3743 // output col frame cache |
|
3744 printf("\n col frame cache ->"); |
|
3745 for (colX = 0; colX < numCols; colX++) { |
|
3746 nsTableColFrame* colFrame = mColFrames.ElementAt(colX); |
|
3747 if (0 == (colX % 8)) { |
|
3748 printf("\n"); |
|
3749 } |
|
3750 printf ("%d=%p ", colX, static_cast<void*>(colFrame)); |
|
3751 nsTableColType colType = colFrame->GetColType(); |
|
3752 switch (colType) { |
|
3753 case eColContent: |
|
3754 printf(" content "); |
|
3755 break; |
|
3756 case eColAnonymousCol: |
|
3757 printf(" anonymous-column "); |
|
3758 break; |
|
3759 case eColAnonymousColGroup: |
|
3760 printf(" anonymous-colgroup "); |
|
3761 break; |
|
3762 case eColAnonymousCell: |
|
3763 printf(" anonymous-cell "); |
|
3764 break; |
|
3765 } |
|
3766 } |
|
3767 printf("\n colgroups->"); |
|
3768 for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame; |
|
3769 childFrame = childFrame->GetNextSibling()) { |
|
3770 if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) { |
|
3771 nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame; |
|
3772 colGroupFrame->Dump(1); |
|
3773 } |
|
3774 } |
|
3775 for (colX = 0; colX < numCols; colX++) { |
|
3776 printf("\n"); |
|
3777 nsTableColFrame* colFrame = GetColFrame(colX); |
|
3778 colFrame->Dump(1); |
|
3779 } |
|
3780 } |
|
3781 if (aDumpCellMap) { |
|
3782 nsTableCellMap* cellMap = GetCellMap(); |
|
3783 cellMap->Dump(); |
|
3784 } |
|
3785 printf(" ***END TABLE DUMP*** \n"); |
|
3786 } |
|
3787 #endif |
|
3788 |
|
3789 // nsTableIterator |
|
3790 nsTableIterator::nsTableIterator(nsIFrame& aSource) |
|
3791 { |
|
3792 nsIFrame* firstChild = aSource.GetFirstPrincipalChild(); |
|
3793 Init(firstChild); |
|
3794 } |
|
3795 |
|
3796 nsTableIterator::nsTableIterator(nsFrameList& aSource) |
|
3797 { |
|
3798 nsIFrame* firstChild = aSource.FirstChild(); |
|
3799 Init(firstChild); |
|
3800 } |
|
3801 |
|
3802 void nsTableIterator::Init(nsIFrame* aFirstChild) |
|
3803 { |
|
3804 mFirstListChild = aFirstChild; |
|
3805 mFirstChild = aFirstChild; |
|
3806 mCurrentChild = nullptr; |
|
3807 mLeftToRight = true; |
|
3808 mCount = -1; |
|
3809 |
|
3810 if (!mFirstChild) { |
|
3811 return; |
|
3812 } |
|
3813 |
|
3814 nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild); |
|
3815 mLeftToRight = (NS_STYLE_DIRECTION_LTR == |
|
3816 table->StyleVisibility()->mDirection); |
|
3817 |
|
3818 if (!mLeftToRight) { |
|
3819 mCount = 0; |
|
3820 nsIFrame* nextChild = mFirstChild->GetNextSibling(); |
|
3821 while (nullptr != nextChild) { |
|
3822 mCount++; |
|
3823 mFirstChild = nextChild; |
|
3824 nextChild = nextChild->GetNextSibling(); |
|
3825 } |
|
3826 } |
|
3827 } |
|
3828 |
|
3829 nsIFrame* nsTableIterator::First() |
|
3830 { |
|
3831 mCurrentChild = mFirstChild; |
|
3832 return mCurrentChild; |
|
3833 } |
|
3834 |
|
3835 nsIFrame* nsTableIterator::Next() |
|
3836 { |
|
3837 if (!mCurrentChild) { |
|
3838 return nullptr; |
|
3839 } |
|
3840 |
|
3841 if (mLeftToRight) { |
|
3842 mCurrentChild = mCurrentChild->GetNextSibling(); |
|
3843 return mCurrentChild; |
|
3844 } |
|
3845 else { |
|
3846 nsIFrame* targetChild = mCurrentChild; |
|
3847 mCurrentChild = nullptr; |
|
3848 nsIFrame* child = mFirstListChild; |
|
3849 while (child && (child != targetChild)) { |
|
3850 mCurrentChild = child; |
|
3851 child = child->GetNextSibling(); |
|
3852 } |
|
3853 return mCurrentChild; |
|
3854 } |
|
3855 } |
|
3856 |
|
3857 bool nsTableIterator::IsLeftToRight() |
|
3858 { |
|
3859 return mLeftToRight; |
|
3860 } |
|
3861 |
|
3862 int32_t nsTableIterator::Count() |
|
3863 { |
|
3864 if (-1 == mCount) { |
|
3865 mCount = 0; |
|
3866 nsIFrame* child = mFirstListChild; |
|
3867 while (nullptr != child) { |
|
3868 mCount++; |
|
3869 child = child->GetNextSibling(); |
|
3870 } |
|
3871 } |
|
3872 return mCount; |
|
3873 } |
|
3874 |
|
3875 bool |
|
3876 nsTableFrame::ColumnHasCellSpacingBefore(int32_t aColIndex) const |
|
3877 { |
|
3878 // Since fixed-layout tables should not have their column sizes change |
|
3879 // as they load, we assume that all columns are significant. |
|
3880 if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed) |
|
3881 return true; |
|
3882 // the first column is always significant |
|
3883 if (aColIndex == 0) |
|
3884 return true; |
|
3885 nsTableCellMap* cellMap = GetCellMap(); |
|
3886 if (!cellMap) |
|
3887 return false; |
|
3888 return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0; |
|
3889 } |
|
3890 |
|
3891 /******************************************************************************** |
|
3892 * Collapsing Borders |
|
3893 * |
|
3894 * The CSS spec says to resolve border conflicts in this order: |
|
3895 * 1) any border with the style HIDDEN wins |
|
3896 * 2) the widest border with a style that is not NONE wins |
|
3897 * 3) the border styles are ranked in this order, highest to lowest precedence: |
|
3898 * double, solid, dashed, dotted, ridge, outset, groove, inset |
|
3899 * 4) borders that are of equal width and style (differ only in color) have this precedence: |
|
3900 * cell, row, rowgroup, col, colgroup, table |
|
3901 * 5) if all border styles are NONE, then that's the computed border style. |
|
3902 *******************************************************************************/ |
|
3903 |
|
3904 #ifdef DEBUG |
|
3905 #define VerifyNonNegativeDamageRect(r) \ |
|
3906 NS_ASSERTION((r).x >= 0, "negative col index"); \ |
|
3907 NS_ASSERTION((r).y >= 0, "negative row index"); \ |
|
3908 NS_ASSERTION((r).width >= 0, "negative horizontal damage"); \ |
|
3909 NS_ASSERTION((r).height >= 0, "negative vertical damage"); |
|
3910 #define VerifyDamageRect(r) \ |
|
3911 VerifyNonNegativeDamageRect(r); \ |
|
3912 NS_ASSERTION((r).XMost() <= GetColCount(), \ |
|
3913 "horizontal damage extends outside table"); \ |
|
3914 NS_ASSERTION((r).YMost() <= GetRowCount(), \ |
|
3915 "vertical damage extends outside table"); |
|
3916 #endif |
|
3917 |
|
3918 void |
|
3919 nsTableFrame::AddBCDamageArea(const nsIntRect& aValue) |
|
3920 { |
|
3921 NS_ASSERTION(IsBorderCollapse(), "invalid AddBCDamageArea call"); |
|
3922 #ifdef DEBUG |
|
3923 VerifyDamageRect(aValue); |
|
3924 #endif |
|
3925 |
|
3926 SetNeedToCalcBCBorders(true); |
|
3927 // Get the property |
|
3928 BCPropertyData* value = GetBCProperty(true); |
|
3929 if (value) { |
|
3930 #ifdef DEBUG |
|
3931 VerifyNonNegativeDamageRect(value->mDamageArea); |
|
3932 #endif |
|
3933 // Clamp the old damage area to the current table area in case it shrunk. |
|
3934 int32_t cols = GetColCount(); |
|
3935 if (value->mDamageArea.XMost() > cols) { |
|
3936 if (value->mDamageArea.x > cols) { |
|
3937 value->mDamageArea.x = cols; |
|
3938 value->mDamageArea.width = 0; |
|
3939 } |
|
3940 else { |
|
3941 value->mDamageArea.width = cols - value->mDamageArea.x; |
|
3942 } |
|
3943 } |
|
3944 int32_t rows = GetRowCount(); |
|
3945 if (value->mDamageArea.YMost() > rows) { |
|
3946 if (value->mDamageArea.y > rows) { |
|
3947 value->mDamageArea.y = rows; |
|
3948 value->mDamageArea.height = 0; |
|
3949 } |
|
3950 else { |
|
3951 value->mDamageArea.height = rows - value->mDamageArea.y; |
|
3952 } |
|
3953 } |
|
3954 |
|
3955 // Construct a union of the new and old damage areas. |
|
3956 value->mDamageArea.UnionRect(value->mDamageArea, aValue); |
|
3957 } |
|
3958 } |
|
3959 |
|
3960 |
|
3961 void |
|
3962 nsTableFrame::SetFullBCDamageArea() |
|
3963 { |
|
3964 NS_ASSERTION(IsBorderCollapse(), "invalid SetFullBCDamageArea call"); |
|
3965 |
|
3966 SetNeedToCalcBCBorders(true); |
|
3967 |
|
3968 BCPropertyData* value = GetBCProperty(true); |
|
3969 if (value) { |
|
3970 value->mDamageArea = nsIntRect(0, 0, GetColCount(), GetRowCount()); |
|
3971 } |
|
3972 } |
|
3973 |
|
3974 |
|
3975 /* BCCellBorder represents a border segment which can be either a horizontal |
|
3976 * or a vertical segment. For each segment we need to know the color, width, |
|
3977 * style, who owns it and how long it is in cellmap coordinates. |
|
3978 * Ownership of these segments is important to calculate which corners should |
|
3979 * be bevelled. This structure has dual use, its used first to compute the |
|
3980 * dominant border for horizontal and vertical segments and to store the |
|
3981 * preliminary computed border results in the BCCellBorders structure. |
|
3982 * This temporary storage is not symmetric with respect to horizontal and |
|
3983 * vertical border segments, its always column oriented. For each column in |
|
3984 * the cellmap there is a temporary stored vertical and horizontal segment. |
|
3985 * XXX_Bernd this asymmetry is the root of those rowspan bc border errors |
|
3986 */ |
|
3987 struct BCCellBorder |
|
3988 { |
|
3989 BCCellBorder() { Reset(0, 1); } |
|
3990 void Reset(uint32_t aRowIndex, uint32_t aRowSpan); |
|
3991 nscolor color; // border segment color |
|
3992 BCPixelSize width; // border segment width in pixel coordinates !! |
|
3993 uint8_t style; // border segment style, possible values are defined |
|
3994 // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_* |
|
3995 BCBorderOwner owner; // border segment owner, possible values are defined |
|
3996 // in celldata.h. In the cellmap for each border |
|
3997 // segment we store the owner and later when |
|
3998 // painting we know the owner and can retrieve the |
|
3999 // style info from the corresponding frame |
|
4000 int32_t rowIndex; // rowIndex of temporary stored horizontal border |
|
4001 // segments relative to the table |
|
4002 int32_t rowSpan; // row span of temporary stored horizontal border |
|
4003 // segments |
|
4004 }; |
|
4005 |
|
4006 void |
|
4007 BCCellBorder::Reset(uint32_t aRowIndex, |
|
4008 uint32_t aRowSpan) |
|
4009 { |
|
4010 style = NS_STYLE_BORDER_STYLE_NONE; |
|
4011 color = 0; |
|
4012 width = 0; |
|
4013 owner = eTableOwner; |
|
4014 rowIndex = aRowIndex; |
|
4015 rowSpan = aRowSpan; |
|
4016 } |
|
4017 |
|
4018 class BCMapCellIterator; |
|
4019 |
|
4020 /***************************************************************** |
|
4021 * BCMapCellInfo |
|
4022 * This structure stores information about the cellmap and all involved |
|
4023 * table related frames that are used during the computation of winning borders |
|
4024 * in CalcBCBorders so that they do need to be looked up again and again when |
|
4025 * iterating over the cells. |
|
4026 ****************************************************************/ |
|
4027 struct BCMapCellInfo |
|
4028 { |
|
4029 BCMapCellInfo(nsTableFrame* aTableFrame); |
|
4030 void ResetCellInfo(); |
|
4031 void SetInfo(nsTableRowFrame* aNewRow, |
|
4032 int32_t aColIndex, |
|
4033 BCCellData* aCellData, |
|
4034 BCMapCellIterator* aIter, |
|
4035 nsCellMap* aCellMap = nullptr); |
|
4036 // The BCMapCellInfo has functions to set the continous |
|
4037 // border widths (see nsTablePainter.cpp for a description of the continous |
|
4038 // borders concept). The widths are computed inside these functions based on |
|
4039 // the current position inside the table and the cached frames that correspond |
|
4040 // to this position. The widths are stored in member variables of the internal |
|
4041 // table frames. |
|
4042 void SetTableTopLeftContBCBorder(); |
|
4043 void SetRowGroupLeftContBCBorder(); |
|
4044 void SetRowGroupRightContBCBorder(); |
|
4045 void SetRowGroupBottomContBCBorder(); |
|
4046 void SetRowLeftContBCBorder(); |
|
4047 void SetRowRightContBCBorder(); |
|
4048 void SetColumnTopRightContBCBorder(); |
|
4049 void SetColumnBottomContBCBorder(); |
|
4050 void SetColGroupBottomContBCBorder(); |
|
4051 void SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, |
|
4052 nsTableRowFrame* aNextRow); |
|
4053 |
|
4054 // functions to set the border widths on the table related frames, where the |
|
4055 // knowledge about the current position in the table is used. |
|
4056 void SetTableTopBorderWidth(BCPixelSize aWidth); |
|
4057 void SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth); |
|
4058 void SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth); |
|
4059 void SetTableBottomBorderWidth(BCPixelSize aWidth); |
|
4060 void SetLeftBorderWidths(BCPixelSize aWidth); |
|
4061 void SetRightBorderWidths(BCPixelSize aWidth); |
|
4062 void SetTopBorderWidths(BCPixelSize aWidth); |
|
4063 void SetBottomBorderWidths(BCPixelSize aWidth); |
|
4064 |
|
4065 // functions to compute the borders; they depend on the |
|
4066 // knowledge about the current position in the table. The edge functions |
|
4067 // should be called if a table edge is involved, otherwise the internal |
|
4068 // functions should be called. |
|
4069 BCCellBorder GetTopEdgeBorder(); |
|
4070 BCCellBorder GetBottomEdgeBorder(); |
|
4071 BCCellBorder GetLeftEdgeBorder(); |
|
4072 BCCellBorder GetRightEdgeBorder(); |
|
4073 BCCellBorder GetRightInternalBorder(); |
|
4074 BCCellBorder GetLeftInternalBorder(); |
|
4075 BCCellBorder GetTopInternalBorder(); |
|
4076 BCCellBorder GetBottomInternalBorder(); |
|
4077 |
|
4078 // functions to set the interal position information |
|
4079 void SetColumn(int32_t aColX); |
|
4080 // Increment the row as we loop over the rows of a rowspan |
|
4081 void IncrementRow(bool aResetToTopRowOfCell = false); |
|
4082 |
|
4083 // Helper functions to get extent of the cell |
|
4084 int32_t GetCellEndRowIndex() const; |
|
4085 int32_t GetCellEndColIndex() const; |
|
4086 |
|
4087 // storage of table information |
|
4088 nsTableFrame* mTableFrame; |
|
4089 int32_t mNumTableRows; |
|
4090 int32_t mNumTableCols; |
|
4091 BCPropertyData* mTableBCData; |
|
4092 |
|
4093 // storage of table ltr information, the border collapse code swaps the sides |
|
4094 // to account for rtl tables, this is done through mStartSide and mEndSide |
|
4095 bool mTableIsLTR; |
|
4096 mozilla::css::Side mStartSide; |
|
4097 mozilla::css::Side mEndSide; |
|
4098 |
|
4099 // a cell can only belong to one rowgroup |
|
4100 nsTableRowGroupFrame* mRowGroup; |
|
4101 |
|
4102 // a cell with a rowspan has a top and a bottom row, and rows in between |
|
4103 nsTableRowFrame* mTopRow; |
|
4104 nsTableRowFrame* mBottomRow; |
|
4105 nsTableRowFrame* mCurrentRowFrame; |
|
4106 |
|
4107 // a cell with a colspan has a left and right column and columns in between |
|
4108 // they can belong to different colgroups |
|
4109 nsTableColGroupFrame* mColGroup; |
|
4110 nsTableColGroupFrame* mCurrentColGroupFrame; |
|
4111 |
|
4112 nsTableColFrame* mLeftCol; |
|
4113 nsTableColFrame* mRightCol; |
|
4114 nsTableColFrame* mCurrentColFrame; |
|
4115 |
|
4116 // cell information |
|
4117 BCCellData* mCellData; |
|
4118 nsBCTableCellFrame* mCell; |
|
4119 |
|
4120 int32_t mRowIndex; |
|
4121 int32_t mRowSpan; |
|
4122 int32_t mColIndex; |
|
4123 int32_t mColSpan; |
|
4124 |
|
4125 // flags to describe the position of the cell with respect to the row- and |
|
4126 // colgroups, for instance mRgAtTop documents that the top cell border hits |
|
4127 // a rowgroup border |
|
4128 bool mRgAtTop; |
|
4129 bool mRgAtBottom; |
|
4130 bool mCgAtLeft; |
|
4131 bool mCgAtRight; |
|
4132 |
|
4133 }; |
|
4134 |
|
4135 |
|
4136 BCMapCellInfo::BCMapCellInfo(nsTableFrame* aTableFrame) |
|
4137 { |
|
4138 mTableFrame = aTableFrame; |
|
4139 mTableIsLTR = |
|
4140 aTableFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR; |
|
4141 if (mTableIsLTR) { |
|
4142 mStartSide = NS_SIDE_LEFT; |
|
4143 mEndSide = NS_SIDE_RIGHT; |
|
4144 } |
|
4145 else { |
|
4146 mStartSide = NS_SIDE_RIGHT; |
|
4147 mEndSide = NS_SIDE_LEFT; |
|
4148 } |
|
4149 mNumTableRows = mTableFrame->GetRowCount(); |
|
4150 mNumTableCols = mTableFrame->GetColCount(); |
|
4151 mTableBCData = static_cast<BCPropertyData*> |
|
4152 (mTableFrame->Properties().Get(TableBCProperty())); |
|
4153 |
|
4154 ResetCellInfo(); |
|
4155 } |
|
4156 |
|
4157 void BCMapCellInfo::ResetCellInfo() |
|
4158 { |
|
4159 mCellData = nullptr; |
|
4160 mRowGroup = nullptr; |
|
4161 mTopRow = nullptr; |
|
4162 mBottomRow = nullptr; |
|
4163 mColGroup = nullptr; |
|
4164 mLeftCol = nullptr; |
|
4165 mRightCol = nullptr; |
|
4166 mCell = nullptr; |
|
4167 mRowIndex = mRowSpan = mColIndex = mColSpan = 0; |
|
4168 mRgAtTop = mRgAtBottom = mCgAtLeft = mCgAtRight = false; |
|
4169 } |
|
4170 |
|
4171 inline int32_t BCMapCellInfo::GetCellEndRowIndex() const |
|
4172 { |
|
4173 return mRowIndex + mRowSpan - 1; |
|
4174 } |
|
4175 |
|
4176 inline int32_t BCMapCellInfo::GetCellEndColIndex() const |
|
4177 { |
|
4178 return mColIndex + mColSpan - 1; |
|
4179 } |
|
4180 |
|
4181 |
|
4182 class BCMapCellIterator |
|
4183 { |
|
4184 public: |
|
4185 BCMapCellIterator(nsTableFrame* aTableFrame, |
|
4186 const nsIntRect& aDamageArea); |
|
4187 |
|
4188 void First(BCMapCellInfo& aMapCellInfo); |
|
4189 |
|
4190 void Next(BCMapCellInfo& aMapCellInfo); |
|
4191 |
|
4192 void PeekRight(BCMapCellInfo& aRefInfo, |
|
4193 uint32_t aRowIndex, |
|
4194 BCMapCellInfo& aAjaInfo); |
|
4195 |
|
4196 void PeekBottom(BCMapCellInfo& aRefInfo, |
|
4197 uint32_t aColIndex, |
|
4198 BCMapCellInfo& aAjaInfo); |
|
4199 |
|
4200 bool IsNewRow() { return mIsNewRow; } |
|
4201 |
|
4202 nsTableRowFrame* GetPrevRow() const { return mPrevRow; } |
|
4203 nsTableRowFrame* GetCurrentRow() const { return mRow; } |
|
4204 nsTableRowGroupFrame* GetCurrentRowGroup() const { return mRowGroup;} |
|
4205 |
|
4206 int32_t mRowGroupStart; |
|
4207 int32_t mRowGroupEnd; |
|
4208 bool mAtEnd; |
|
4209 nsCellMap* mCellMap; |
|
4210 |
|
4211 private: |
|
4212 bool SetNewRow(nsTableRowFrame* row = nullptr); |
|
4213 bool SetNewRowGroup(bool aFindFirstDamagedRow); |
|
4214 |
|
4215 nsTableFrame* mTableFrame; |
|
4216 nsTableCellMap* mTableCellMap; |
|
4217 nsTableFrame::RowGroupArray mRowGroups; |
|
4218 nsTableRowGroupFrame* mRowGroup; |
|
4219 int32_t mRowGroupIndex; |
|
4220 uint32_t mNumTableRows; |
|
4221 nsTableRowFrame* mRow; |
|
4222 nsTableRowFrame* mPrevRow; |
|
4223 bool mIsNewRow; |
|
4224 int32_t mRowIndex; |
|
4225 uint32_t mNumTableCols; |
|
4226 int32_t mColIndex; |
|
4227 nsPoint mAreaStart; |
|
4228 nsPoint mAreaEnd; |
|
4229 }; |
|
4230 |
|
4231 BCMapCellIterator::BCMapCellIterator(nsTableFrame* aTableFrame, |
|
4232 const nsIntRect& aDamageArea) |
|
4233 :mTableFrame(aTableFrame) |
|
4234 { |
|
4235 mTableCellMap = aTableFrame->GetCellMap(); |
|
4236 |
|
4237 mAreaStart.x = aDamageArea.x; |
|
4238 mAreaStart.y = aDamageArea.y; |
|
4239 mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1; |
|
4240 mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1; |
|
4241 |
|
4242 mNumTableRows = mTableFrame->GetRowCount(); |
|
4243 mRow = nullptr; |
|
4244 mRowIndex = 0; |
|
4245 mNumTableCols = mTableFrame->GetColCount(); |
|
4246 mColIndex = 0; |
|
4247 mRowGroupIndex = -1; |
|
4248 |
|
4249 // Get the ordered row groups |
|
4250 aTableFrame->OrderRowGroups(mRowGroups); |
|
4251 |
|
4252 mAtEnd = true; // gets reset when First() is called |
|
4253 } |
|
4254 |
|
4255 // fill fields that we need for border collapse computation on a given cell |
|
4256 void |
|
4257 BCMapCellInfo::SetInfo(nsTableRowFrame* aNewRow, |
|
4258 int32_t aColIndex, |
|
4259 BCCellData* aCellData, |
|
4260 BCMapCellIterator* aIter, |
|
4261 nsCellMap* aCellMap) |
|
4262 { |
|
4263 // fill the cell information |
|
4264 mCellData = aCellData; |
|
4265 mColIndex = aColIndex; |
|
4266 |
|
4267 // initialize the row information if it was not previously set for cells in |
|
4268 // this row |
|
4269 mRowIndex = 0; |
|
4270 if (aNewRow) { |
|
4271 mTopRow = aNewRow; |
|
4272 mRowIndex = aNewRow->GetRowIndex(); |
|
4273 } |
|
4274 |
|
4275 // fill cell frame info and row information |
|
4276 mCell = nullptr; |
|
4277 mRowSpan = 1; |
|
4278 mColSpan = 1; |
|
4279 if (aCellData) { |
|
4280 mCell = static_cast<nsBCTableCellFrame*>(aCellData->GetCellFrame()); |
|
4281 if (mCell) { |
|
4282 if (!mTopRow) { |
|
4283 mTopRow = static_cast<nsTableRowFrame*>(mCell->GetParent()); |
|
4284 if (!mTopRow) ABORT0(); |
|
4285 mRowIndex = mTopRow->GetRowIndex(); |
|
4286 } |
|
4287 mColSpan = mTableFrame->GetEffectiveColSpan(*mCell, aCellMap); |
|
4288 mRowSpan = mTableFrame->GetEffectiveRowSpan(*mCell, aCellMap); |
|
4289 } |
|
4290 } |
|
4291 |
|
4292 if (!mTopRow) { |
|
4293 mTopRow = aIter->GetCurrentRow(); |
|
4294 } |
|
4295 if (1 == mRowSpan) { |
|
4296 mBottomRow = mTopRow; |
|
4297 } |
|
4298 else { |
|
4299 mBottomRow = mTopRow->GetNextRow(); |
|
4300 if (mBottomRow) { |
|
4301 for (int32_t spanY = 2; mBottomRow && (spanY < mRowSpan); spanY++) { |
|
4302 mBottomRow = mBottomRow->GetNextRow(); |
|
4303 } |
|
4304 NS_ASSERTION(mBottomRow, "spanned row not found"); |
|
4305 } |
|
4306 else { |
|
4307 NS_ASSERTION(false, "error in cell map"); |
|
4308 mRowSpan = 1; |
|
4309 mBottomRow = mTopRow; |
|
4310 } |
|
4311 } |
|
4312 // row group frame info |
|
4313 // try to reuse the rgStart and rgEnd from the iterator as calls to |
|
4314 // GetRowCount() are computationally expensive and should be avoided if |
|
4315 // possible |
|
4316 uint32_t rgStart = aIter->mRowGroupStart; |
|
4317 uint32_t rgEnd = aIter->mRowGroupEnd; |
|
4318 mRowGroup = static_cast<nsTableRowGroupFrame*>(mTopRow->GetParent()); |
|
4319 if (mRowGroup != aIter->GetCurrentRowGroup()) { |
|
4320 rgStart = mRowGroup->GetStartRowIndex(); |
|
4321 rgEnd = rgStart + mRowGroup->GetRowCount() - 1; |
|
4322 } |
|
4323 uint32_t rowIndex = mTopRow->GetRowIndex(); |
|
4324 mRgAtTop = (rgStart == rowIndex); |
|
4325 mRgAtBottom = (rgEnd == rowIndex + mRowSpan - 1); |
|
4326 |
|
4327 // col frame info |
|
4328 mLeftCol = mTableFrame->GetColFrame(aColIndex); |
|
4329 if (!mLeftCol) ABORT0(); |
|
4330 |
|
4331 mRightCol = mLeftCol; |
|
4332 if (mColSpan > 1) { |
|
4333 nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + |
|
4334 mColSpan -1); |
|
4335 if (!colFrame) ABORT0(); |
|
4336 mRightCol = colFrame; |
|
4337 } |
|
4338 |
|
4339 // col group frame info |
|
4340 mColGroup = static_cast<nsTableColGroupFrame*>(mLeftCol->GetParent()); |
|
4341 int32_t cgStart = mColGroup->GetStartColumnIndex(); |
|
4342 int32_t cgEnd = std::max(0, cgStart + mColGroup->GetColCount() - 1); |
|
4343 mCgAtLeft = (cgStart == aColIndex); |
|
4344 mCgAtRight = (cgEnd == aColIndex + mColSpan - 1); |
|
4345 } |
|
4346 |
|
4347 bool |
|
4348 BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow) |
|
4349 { |
|
4350 mAtEnd = true; |
|
4351 mPrevRow = mRow; |
|
4352 if (aRow) { |
|
4353 mRow = aRow; |
|
4354 } |
|
4355 else if (mRow) { |
|
4356 mRow = mRow->GetNextRow(); |
|
4357 } |
|
4358 if (mRow) { |
|
4359 mRowIndex = mRow->GetRowIndex(); |
|
4360 // get to the first entry with an originating cell |
|
4361 int32_t rgRowIndex = mRowIndex - mRowGroupStart; |
|
4362 if (uint32_t(rgRowIndex) >= mCellMap->mRows.Length()) |
|
4363 ABORT1(false); |
|
4364 const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex]; |
|
4365 |
|
4366 for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) { |
|
4367 CellData* cellData = row.SafeElementAt(mColIndex); |
|
4368 if (!cellData) { // add a dead cell data |
|
4369 nsIntRect damageArea; |
|
4370 cellData = mCellMap->AppendCell(*mTableCellMap, nullptr, rgRowIndex, |
|
4371 false, 0, damageArea); |
|
4372 if (!cellData) ABORT1(false); |
|
4373 } |
|
4374 if (cellData && (cellData->IsOrig() || cellData->IsDead())) { |
|
4375 break; |
|
4376 } |
|
4377 } |
|
4378 mIsNewRow = true; |
|
4379 mAtEnd = false; |
|
4380 } |
|
4381 else ABORT1(false); |
|
4382 |
|
4383 return !mAtEnd; |
|
4384 } |
|
4385 |
|
4386 bool |
|
4387 BCMapCellIterator::SetNewRowGroup(bool aFindFirstDamagedRow) |
|
4388 { |
|
4389 mAtEnd = true; |
|
4390 int32_t numRowGroups = mRowGroups.Length(); |
|
4391 mCellMap = nullptr; |
|
4392 for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) { |
|
4393 mRowGroup = mRowGroups[mRowGroupIndex]; |
|
4394 int32_t rowCount = mRowGroup->GetRowCount(); |
|
4395 mRowGroupStart = mRowGroup->GetStartRowIndex(); |
|
4396 mRowGroupEnd = mRowGroupStart + rowCount - 1; |
|
4397 if (rowCount > 0) { |
|
4398 mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap); |
|
4399 if (!mCellMap) ABORT1(false); |
|
4400 nsTableRowFrame* firstRow = mRowGroup->GetFirstRow(); |
|
4401 if (aFindFirstDamagedRow) { |
|
4402 if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { |
|
4403 // the damage area starts in the row group |
|
4404 if (aFindFirstDamagedRow) { |
|
4405 // find the correct first damaged row |
|
4406 int32_t numRows = mAreaStart.y - mRowGroupStart; |
|
4407 for (int32_t i = 0; i < numRows; i++) { |
|
4408 firstRow = firstRow->GetNextRow(); |
|
4409 if (!firstRow) ABORT1(false); |
|
4410 } |
|
4411 } |
|
4412 } |
|
4413 else { |
|
4414 continue; |
|
4415 } |
|
4416 } |
|
4417 if (SetNewRow(firstRow)) { // sets mAtEnd |
|
4418 break; |
|
4419 } |
|
4420 } |
|
4421 } |
|
4422 |
|
4423 return !mAtEnd; |
|
4424 } |
|
4425 |
|
4426 void |
|
4427 BCMapCellIterator::First(BCMapCellInfo& aMapInfo) |
|
4428 { |
|
4429 aMapInfo.ResetCellInfo(); |
|
4430 |
|
4431 SetNewRowGroup(true); // sets mAtEnd |
|
4432 while (!mAtEnd) { |
|
4433 if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) { |
|
4434 BCCellData* cellData = |
|
4435 static_cast<BCCellData*>(mCellMap->GetDataAt(mAreaStart.y - |
|
4436 mRowGroupStart, |
|
4437 mAreaStart.x)); |
|
4438 if (cellData && (cellData->IsOrig() || cellData->IsDead())) { |
|
4439 aMapInfo.SetInfo(mRow, mAreaStart.x, cellData, this); |
|
4440 return; |
|
4441 } |
|
4442 else { |
|
4443 NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) , |
|
4444 "damage area expanded incorrectly"); |
|
4445 } |
|
4446 } |
|
4447 SetNewRowGroup(true); // sets mAtEnd |
|
4448 } |
|
4449 } |
|
4450 |
|
4451 void |
|
4452 BCMapCellIterator::Next(BCMapCellInfo& aMapInfo) |
|
4453 { |
|
4454 if (mAtEnd) ABORT0(); |
|
4455 aMapInfo.ResetCellInfo(); |
|
4456 |
|
4457 mIsNewRow = false; |
|
4458 mColIndex++; |
|
4459 while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) { |
|
4460 for (; mColIndex <= mAreaEnd.x; mColIndex++) { |
|
4461 int32_t rgRowIndex = mRowIndex - mRowGroupStart; |
|
4462 BCCellData* cellData = |
|
4463 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, mColIndex)); |
|
4464 if (!cellData) { // add a dead cell data |
|
4465 nsIntRect damageArea; |
|
4466 cellData = |
|
4467 static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr, |
|
4468 rgRowIndex, false, 0, |
|
4469 damageArea)); |
|
4470 if (!cellData) ABORT0(); |
|
4471 } |
|
4472 if (cellData && (cellData->IsOrig() || cellData->IsDead())) { |
|
4473 aMapInfo.SetInfo(mRow, mColIndex, cellData, this); |
|
4474 return; |
|
4475 } |
|
4476 } |
|
4477 if (mRowIndex >= mRowGroupEnd) { |
|
4478 SetNewRowGroup(false); // could set mAtEnd |
|
4479 } |
|
4480 else { |
|
4481 SetNewRow(); // could set mAtEnd |
|
4482 } |
|
4483 } |
|
4484 mAtEnd = true; |
|
4485 } |
|
4486 |
|
4487 void |
|
4488 BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo, |
|
4489 uint32_t aRowIndex, |
|
4490 BCMapCellInfo& aAjaInfo) |
|
4491 { |
|
4492 aAjaInfo.ResetCellInfo(); |
|
4493 int32_t colIndex = aRefInfo.mColIndex + aRefInfo.mColSpan; |
|
4494 uint32_t rgRowIndex = aRowIndex - mRowGroupStart; |
|
4495 |
|
4496 BCCellData* cellData = |
|
4497 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex)); |
|
4498 if (!cellData) { // add a dead cell data |
|
4499 NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error"); |
|
4500 nsIntRect damageArea; |
|
4501 cellData = |
|
4502 static_cast<BCCellData*>(mCellMap->AppendCell(*mTableCellMap, nullptr, |
|
4503 rgRowIndex, false, 0, |
|
4504 damageArea)); |
|
4505 if (!cellData) ABORT0(); |
|
4506 } |
|
4507 nsTableRowFrame* row = nullptr; |
|
4508 if (cellData->IsRowSpan()) { |
|
4509 rgRowIndex -= cellData->GetRowSpanOffset(); |
|
4510 cellData = |
|
4511 static_cast<BCCellData*>(mCellMap->GetDataAt(rgRowIndex, colIndex)); |
|
4512 if (!cellData) |
|
4513 ABORT0(); |
|
4514 } |
|
4515 else { |
|
4516 row = mRow; |
|
4517 } |
|
4518 aAjaInfo.SetInfo(row, colIndex, cellData, this); |
|
4519 } |
|
4520 |
|
4521 void |
|
4522 BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo, |
|
4523 uint32_t aColIndex, |
|
4524 BCMapCellInfo& aAjaInfo) |
|
4525 { |
|
4526 aAjaInfo.ResetCellInfo(); |
|
4527 int32_t rowIndex = aRefInfo.mRowIndex + aRefInfo.mRowSpan; |
|
4528 int32_t rgRowIndex = rowIndex - mRowGroupStart; |
|
4529 nsTableRowGroupFrame* rg = mRowGroup; |
|
4530 nsCellMap* cellMap = mCellMap; |
|
4531 nsTableRowFrame* nextRow = nullptr; |
|
4532 if (rowIndex > mRowGroupEnd) { |
|
4533 int32_t nextRgIndex = mRowGroupIndex; |
|
4534 do { |
|
4535 nextRgIndex++; |
|
4536 rg = mRowGroups.SafeElementAt(nextRgIndex); |
|
4537 if (rg) { |
|
4538 cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0(); |
|
4539 rgRowIndex = 0; |
|
4540 nextRow = rg->GetFirstRow(); |
|
4541 } |
|
4542 } |
|
4543 while (rg && !nextRow); |
|
4544 if(!rg) return; |
|
4545 } |
|
4546 else { |
|
4547 // get the row within the same row group |
|
4548 nextRow = mRow; |
|
4549 for (int32_t i = 0; i < aRefInfo.mRowSpan; i++) { |
|
4550 nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0(); |
|
4551 } |
|
4552 } |
|
4553 |
|
4554 BCCellData* cellData = |
|
4555 static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex)); |
|
4556 if (!cellData) { // add a dead cell data |
|
4557 NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error"); |
|
4558 nsIntRect damageArea; |
|
4559 cellData = |
|
4560 static_cast<BCCellData*>(cellMap->AppendCell(*mTableCellMap, nullptr, |
|
4561 rgRowIndex, false, 0, |
|
4562 damageArea)); |
|
4563 if (!cellData) ABORT0(); |
|
4564 } |
|
4565 if (cellData->IsColSpan()) { |
|
4566 aColIndex -= cellData->GetColSpanOffset(); |
|
4567 cellData = |
|
4568 static_cast<BCCellData*>(cellMap->GetDataAt(rgRowIndex, aColIndex)); |
|
4569 } |
|
4570 aAjaInfo.SetInfo(nextRow, aColIndex, cellData, this, cellMap); |
|
4571 } |
|
4572 |
|
4573 // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID) |
|
4574 // will return the priority of NS_STYLE_BORDER_STYLE_SOLID. |
|
4575 static uint8_t styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE |
|
4576 2, // NS_STYLE_BORDER_STYLE_GROOVE |
|
4577 4, // NS_STYLE_BORDER_STYLE_RIDGE |
|
4578 5, // NS_STYLE_BORDER_STYLE_DOTTED |
|
4579 6, // NS_STYLE_BORDER_STYLE_DASHED |
|
4580 7, // NS_STYLE_BORDER_STYLE_SOLID |
|
4581 8, // NS_STYLE_BORDER_STYLE_DOUBLE |
|
4582 1, // NS_STYLE_BORDER_STYLE_INSET |
|
4583 3, // NS_STYLE_BORDER_STYLE_OUTSET |
|
4584 9 };// NS_STYLE_BORDER_STYLE_HIDDEN |
|
4585 // priority rules follow CSS 2.1 spec |
|
4586 // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', |
|
4587 // and the lowest: 'inset'. none is even weaker |
|
4588 #define CELL_CORNER true |
|
4589 |
|
4590 /** return the border style, border color for a given frame and side |
|
4591 * @param aFrame - query the info for this frame |
|
4592 * @param aSide - the side of the frame |
|
4593 * @param aStyle - the border style |
|
4594 * @param aColor - the border color |
|
4595 * @param aTableIsLTR - table direction is LTR |
|
4596 */ |
|
4597 static void |
|
4598 GetColorAndStyle(const nsIFrame* aFrame, |
|
4599 mozilla::css::Side aSide, |
|
4600 uint8_t& aStyle, |
|
4601 nscolor& aColor, |
|
4602 bool aTableIsLTR) |
|
4603 { |
|
4604 NS_PRECONDITION(aFrame, "null frame"); |
|
4605 // initialize out arg |
|
4606 aColor = 0; |
|
4607 const nsStyleBorder* styleData = aFrame->StyleBorder(); |
|
4608 if(!aTableIsLTR) { // revert the directions |
|
4609 if (NS_SIDE_RIGHT == aSide) { |
|
4610 aSide = NS_SIDE_LEFT; |
|
4611 } |
|
4612 else if (NS_SIDE_LEFT == aSide) { |
|
4613 aSide = NS_SIDE_RIGHT; |
|
4614 } |
|
4615 } |
|
4616 aStyle = styleData->GetBorderStyle(aSide); |
|
4617 |
|
4618 if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || |
|
4619 (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { |
|
4620 return; |
|
4621 } |
|
4622 aColor = aFrame->StyleContext()->GetVisitedDependentColor( |
|
4623 nsCSSProps::SubpropertyEntryFor(eCSSProperty_border_color)[aSide]); |
|
4624 } |
|
4625 |
|
4626 /** coerce the paint style as required by CSS2.1 |
|
4627 * @param aFrame - query the info for this frame |
|
4628 * @param aSide - the side of the frame |
|
4629 * @param aStyle - the border style |
|
4630 * @param aColor - the border color |
|
4631 * @param aTableIsLTR - table direction is LTR |
|
4632 */ |
|
4633 static void |
|
4634 GetPaintStyleInfo(const nsIFrame* aFrame, |
|
4635 mozilla::css::Side aSide, |
|
4636 uint8_t& aStyle, |
|
4637 nscolor& aColor, |
|
4638 bool aTableIsLTR) |
|
4639 { |
|
4640 GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); |
|
4641 if (NS_STYLE_BORDER_STYLE_INSET == aStyle) { |
|
4642 aStyle = NS_STYLE_BORDER_STYLE_RIDGE; |
|
4643 } |
|
4644 else if (NS_STYLE_BORDER_STYLE_OUTSET == aStyle) { |
|
4645 aStyle = NS_STYLE_BORDER_STYLE_GROOVE; |
|
4646 } |
|
4647 } |
|
4648 |
|
4649 /** return the border style, border color and the width in pixel for a given |
|
4650 * frame and side |
|
4651 * @param aFrame - query the info for this frame |
|
4652 * @param aSide - the side of the frame |
|
4653 * @param aStyle - the border style |
|
4654 * @param aColor - the border color |
|
4655 * @param aTableIsLTR - table direction is LTR |
|
4656 * @param aWidth - the border width in px. |
|
4657 * @param aTwipsToPixels - conversion factor from twips to pixel |
|
4658 */ |
|
4659 static void |
|
4660 GetColorAndStyle(const nsIFrame* aFrame, |
|
4661 mozilla::css::Side aSide, |
|
4662 uint8_t& aStyle, |
|
4663 nscolor& aColor, |
|
4664 bool aTableIsLTR, |
|
4665 BCPixelSize& aWidth) |
|
4666 { |
|
4667 GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR); |
|
4668 if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) || |
|
4669 (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) { |
|
4670 aWidth = 0; |
|
4671 return; |
|
4672 } |
|
4673 const nsStyleBorder* styleData = aFrame->StyleBorder(); |
|
4674 nscoord width; |
|
4675 if(!aTableIsLTR) { // revert the directions |
|
4676 if (NS_SIDE_RIGHT == aSide) { |
|
4677 aSide = NS_SIDE_LEFT; |
|
4678 } |
|
4679 else if (NS_SIDE_LEFT == aSide) { |
|
4680 aSide = NS_SIDE_RIGHT; |
|
4681 } |
|
4682 } |
|
4683 width = styleData->GetComputedBorderWidth(aSide); |
|
4684 aWidth = nsPresContext::AppUnitsToIntCSSPixels(width); |
|
4685 } |
|
4686 |
|
4687 class nsDelayedCalcBCBorders : public nsRunnable { |
|
4688 public: |
|
4689 nsDelayedCalcBCBorders(nsIFrame* aFrame) : |
|
4690 mFrame(aFrame) {} |
|
4691 |
|
4692 NS_IMETHOD Run() MOZ_OVERRIDE { |
|
4693 if (mFrame) { |
|
4694 nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame()); |
|
4695 if (tableFrame->NeedToCalcBCBorders()) { |
|
4696 tableFrame->CalcBCBorders(); |
|
4697 } |
|
4698 } |
|
4699 return NS_OK; |
|
4700 } |
|
4701 private: |
|
4702 nsWeakFrame mFrame; |
|
4703 }; |
|
4704 |
|
4705 bool |
|
4706 nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext, |
|
4707 nsStyleContext* aNewStyleContext) |
|
4708 { |
|
4709 // Attention: the old style context is the one we're forgetting, |
|
4710 // and hence possibly completely bogus for GetStyle* purposes. |
|
4711 // We use PeekStyleData instead. |
|
4712 |
|
4713 const nsStyleBorder* oldStyleData = aOldStyleContext->PeekStyleBorder(); |
|
4714 if (!oldStyleData) |
|
4715 return false; |
|
4716 |
|
4717 const nsStyleBorder* newStyleData = aNewStyleContext->StyleBorder(); |
|
4718 nsChangeHint change = newStyleData->CalcDifference(*oldStyleData); |
|
4719 if (!change) |
|
4720 return false; |
|
4721 if (change & nsChangeHint_NeedReflow) |
|
4722 return true; // the caller only needs to mark the bc damage area |
|
4723 if (change & nsChangeHint_RepaintFrame) { |
|
4724 // we need to recompute the borders and the caller needs to mark |
|
4725 // the bc damage area |
|
4726 // XXX In principle this should only be necessary for border style changes |
|
4727 // However the bc painting code tries to maximize the drawn border segments |
|
4728 // so it stores in the cellmap where a new border segment starts and this |
|
4729 // introduces a unwanted cellmap data dependence on color |
|
4730 nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this); |
|
4731 NS_DispatchToCurrentThread(evt); |
|
4732 return true; |
|
4733 } |
|
4734 return false; |
|
4735 } |
|
4736 |
|
4737 |
|
4738 // Compare two border segments, this comparison depends whether the two |
|
4739 // segments meet at a corner and whether the second segment is horizontal. |
|
4740 // The return value is whichever of aBorder1 or aBorder2 dominates. |
|
4741 static const BCCellBorder& |
|
4742 CompareBorders(bool aIsCorner, // Pass true for corner calculations |
|
4743 const BCCellBorder& aBorder1, |
|
4744 const BCCellBorder& aBorder2, |
|
4745 bool aSecondIsHorizontal, |
|
4746 bool* aFirstDominates = nullptr) |
|
4747 { |
|
4748 bool firstDominates = true; |
|
4749 |
|
4750 if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) { |
|
4751 firstDominates = (aIsCorner) ? false : true; |
|
4752 } |
|
4753 else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) { |
|
4754 firstDominates = (aIsCorner) ? true : false; |
|
4755 } |
|
4756 else if (aBorder1.width < aBorder2.width) { |
|
4757 firstDominates = false; |
|
4758 } |
|
4759 else if (aBorder1.width == aBorder2.width) { |
|
4760 if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) { |
|
4761 firstDominates = false; |
|
4762 } |
|
4763 else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) { |
|
4764 if (aBorder1.owner == aBorder2.owner) { |
|
4765 firstDominates = !aSecondIsHorizontal; |
|
4766 } |
|
4767 else if (aBorder1.owner < aBorder2.owner) { |
|
4768 firstDominates = false; |
|
4769 } |
|
4770 } |
|
4771 } |
|
4772 |
|
4773 if (aFirstDominates) |
|
4774 *aFirstDominates = firstDominates; |
|
4775 |
|
4776 if (firstDominates) |
|
4777 return aBorder1; |
|
4778 return aBorder2; |
|
4779 } |
|
4780 |
|
4781 /** calc the dominant border by considering the table, row/col group, row/col, |
|
4782 * cell. |
|
4783 * Depending on whether the side is vertical or horizontal and whether |
|
4784 * adjacent frames are taken into account the ownership of a single border |
|
4785 * segment is defined. The return value is the dominating border |
|
4786 * The cellmap stores only top and left borders for each cellmap position. |
|
4787 * If the cell border is owned by the cell that is left of the border |
|
4788 * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other |
|
4789 * scenarios with a adjacent owner. |
|
4790 * @param xxxFrame - the frame for style information, might be zero if |
|
4791 * it should not be considered |
|
4792 * @param aSide - side of the frames that should be considered |
|
4793 * @param aAja - the border comparison takes place from the point of |
|
4794 * a frame that is adjacent to the cellmap entry, for |
|
4795 * when a cell owns its lower border it will be the |
|
4796 * adjacent owner as in the cellmap only top and left |
|
4797 * borders are stored. |
|
4798 * @param aTwipsToPixels - conversion factor as borders need to be drawn pixel |
|
4799 * aligned. |
|
4800 */ |
|
4801 static BCCellBorder |
|
4802 CompareBorders(const nsIFrame* aTableFrame, |
|
4803 const nsIFrame* aColGroupFrame, |
|
4804 const nsIFrame* aColFrame, |
|
4805 const nsIFrame* aRowGroupFrame, |
|
4806 const nsIFrame* aRowFrame, |
|
4807 const nsIFrame* aCellFrame, |
|
4808 bool aTableIsLTR, |
|
4809 mozilla::css::Side aSide, |
|
4810 bool aAja) |
|
4811 { |
|
4812 BCCellBorder border, tempBorder; |
|
4813 bool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide); |
|
4814 |
|
4815 // start with the table as dominant if present |
|
4816 if (aTableFrame) { |
|
4817 GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, border.width); |
|
4818 border.owner = eTableOwner; |
|
4819 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
|
4820 return border; |
|
4821 } |
|
4822 } |
|
4823 // see if the colgroup is dominant |
|
4824 if (aColGroupFrame) { |
|
4825 GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
|
4826 tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner; |
|
4827 // pass here and below false for aSecondIsHorizontal as it is only used for corner calculations. |
|
4828 border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
|
4829 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
|
4830 return border; |
|
4831 } |
|
4832 } |
|
4833 // see if the col is dominant |
|
4834 if (aColFrame) { |
|
4835 GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
|
4836 tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner; |
|
4837 border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
|
4838 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
|
4839 return border; |
|
4840 } |
|
4841 } |
|
4842 // see if the rowgroup is dominant |
|
4843 if (aRowGroupFrame) { |
|
4844 GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
|
4845 tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner; |
|
4846 border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
|
4847 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
|
4848 return border; |
|
4849 } |
|
4850 } |
|
4851 // see if the row is dominant |
|
4852 if (aRowFrame) { |
|
4853 GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
|
4854 tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner; |
|
4855 border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
|
4856 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) { |
|
4857 return border; |
|
4858 } |
|
4859 } |
|
4860 // see if the cell is dominant |
|
4861 if (aCellFrame) { |
|
4862 GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, tempBorder.width); |
|
4863 tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner; |
|
4864 border = CompareBorders(!CELL_CORNER, border, tempBorder, false); |
|
4865 } |
|
4866 return border; |
|
4867 } |
|
4868 |
|
4869 static bool |
|
4870 Perpendicular(mozilla::css::Side aSide1, |
|
4871 mozilla::css::Side aSide2) |
|
4872 { |
|
4873 switch (aSide1) { |
|
4874 case NS_SIDE_TOP: |
|
4875 return (NS_SIDE_BOTTOM != aSide2); |
|
4876 case NS_SIDE_RIGHT: |
|
4877 return (NS_SIDE_LEFT != aSide2); |
|
4878 case NS_SIDE_BOTTOM: |
|
4879 return (NS_SIDE_TOP != aSide2); |
|
4880 default: // NS_SIDE_LEFT |
|
4881 return (NS_SIDE_RIGHT != aSide2); |
|
4882 } |
|
4883 } |
|
4884 |
|
4885 // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1 |
|
4886 struct BCCornerInfo |
|
4887 { |
|
4888 BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerElem = subSide = |
|
4889 subElem = hasDashDot = numSegs = bevel = 0; ownerSide = NS_SIDE_TOP; |
|
4890 ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; } |
|
4891 void Set(mozilla::css::Side aSide, |
|
4892 BCCellBorder border); |
|
4893 |
|
4894 void Update(mozilla::css::Side aSide, |
|
4895 BCCellBorder border); |
|
4896 |
|
4897 nscolor ownerColor; // color of borderOwner |
|
4898 uint16_t ownerWidth; // pixel width of borderOwner |
|
4899 uint16_t subWidth; // pixel width of the largest border intersecting the border perpendicular |
|
4900 // to ownerSide |
|
4901 uint32_t ownerSide:2; // mozilla::css::Side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border |
|
4902 // owning the corner relative to the corner |
|
4903 uint32_t ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner |
|
4904 uint32_t ownerStyle:8; // border style of ownerElem |
|
4905 uint32_t subSide:2; // side of border with subWidth relative to the corner |
|
4906 uint32_t subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner |
|
4907 uint32_t subStyle:8; // border style of subElem |
|
4908 uint32_t hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled |
|
4909 uint32_t numSegs:3; // number of segments entering corner |
|
4910 uint32_t bevel:1; // is the corner beveled (uses the above two fields together with subWidth) |
|
4911 uint32_t unused:1; |
|
4912 }; |
|
4913 |
|
4914 void |
|
4915 BCCornerInfo::Set(mozilla::css::Side aSide, |
|
4916 BCCellBorder aBorder) |
|
4917 { |
|
4918 ownerElem = aBorder.owner; |
|
4919 ownerStyle = aBorder.style; |
|
4920 ownerWidth = aBorder.width; |
|
4921 ownerColor = aBorder.color; |
|
4922 ownerSide = aSide; |
|
4923 hasDashDot = 0; |
|
4924 numSegs = 0; |
|
4925 if (aBorder.width > 0) { |
|
4926 numSegs++; |
|
4927 hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) || |
|
4928 (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style); |
|
4929 } |
|
4930 bevel = 0; |
|
4931 subWidth = 0; |
|
4932 // the following will get set later |
|
4933 subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT; |
|
4934 subElem = eTableOwner; |
|
4935 subStyle = NS_STYLE_BORDER_STYLE_SOLID; |
|
4936 } |
|
4937 |
|
4938 void |
|
4939 BCCornerInfo::Update(mozilla::css::Side aSide, |
|
4940 BCCellBorder aBorder) |
|
4941 { |
|
4942 bool existingWins = false; |
|
4943 if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet |
|
4944 Set(aSide, aBorder); |
|
4945 } |
|
4946 else { |
|
4947 bool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner |
|
4948 BCCellBorder oldBorder, tempBorder; |
|
4949 oldBorder.owner = (BCBorderOwner) ownerElem; |
|
4950 oldBorder.style = ownerStyle; |
|
4951 oldBorder.width = ownerWidth; |
|
4952 oldBorder.color = ownerColor; |
|
4953 |
|
4954 mozilla::css::Side oldSide = mozilla::css::Side(ownerSide); |
|
4955 |
|
4956 tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins); |
|
4957 |
|
4958 ownerElem = tempBorder.owner; |
|
4959 ownerStyle = tempBorder.style; |
|
4960 ownerWidth = tempBorder.width; |
|
4961 ownerColor = tempBorder.color; |
|
4962 if (existingWins) { // existing corner is dominant |
|
4963 if (::Perpendicular(mozilla::css::Side(ownerSide), aSide)) { |
|
4964 // see if the new sub info replaces the old |
|
4965 BCCellBorder subBorder; |
|
4966 subBorder.owner = (BCBorderOwner) subElem; |
|
4967 subBorder.style = subStyle; |
|
4968 subBorder.width = subWidth; |
|
4969 subBorder.color = 0; // we are not interested in subBorder color |
|
4970 bool firstWins; |
|
4971 |
|
4972 tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins); |
|
4973 |
|
4974 subElem = tempBorder.owner; |
|
4975 subStyle = tempBorder.style; |
|
4976 subWidth = tempBorder.width; |
|
4977 if (!firstWins) { |
|
4978 subSide = aSide; |
|
4979 } |
|
4980 } |
|
4981 } |
|
4982 else { // input args are dominant |
|
4983 ownerSide = aSide; |
|
4984 if (::Perpendicular(oldSide, mozilla::css::Side(ownerSide))) { |
|
4985 subElem = oldBorder.owner; |
|
4986 subStyle = oldBorder.style; |
|
4987 subWidth = oldBorder.width; |
|
4988 subSide = oldSide; |
|
4989 } |
|
4990 } |
|
4991 if (aBorder.width > 0) { |
|
4992 numSegs++; |
|
4993 if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) || |
|
4994 (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) { |
|
4995 hasDashDot = 1; |
|
4996 } |
|
4997 } |
|
4998 |
|
4999 // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner |
|
5000 bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot); |
|
5001 } |
|
5002 } |
|
5003 |
|
5004 struct BCCorners |
|
5005 { |
|
5006 BCCorners(int32_t aNumCorners, |
|
5007 int32_t aStartIndex); |
|
5008 |
|
5009 ~BCCorners() { delete [] corners; } |
|
5010 |
|
5011 BCCornerInfo& operator [](int32_t i) const |
|
5012 { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); |
|
5013 return corners[clamped(i, startIndex, endIndex) - startIndex]; } |
|
5014 |
|
5015 int32_t startIndex; |
|
5016 int32_t endIndex; |
|
5017 BCCornerInfo* corners; |
|
5018 }; |
|
5019 |
|
5020 BCCorners::BCCorners(int32_t aNumCorners, |
|
5021 int32_t aStartIndex) |
|
5022 { |
|
5023 NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error"); |
|
5024 startIndex = aStartIndex; |
|
5025 endIndex = aStartIndex + aNumCorners - 1; |
|
5026 corners = new BCCornerInfo[aNumCorners]; |
|
5027 } |
|
5028 |
|
5029 |
|
5030 struct BCCellBorders |
|
5031 { |
|
5032 BCCellBorders(int32_t aNumBorders, |
|
5033 int32_t aStartIndex); |
|
5034 |
|
5035 ~BCCellBorders() { delete [] borders; } |
|
5036 |
|
5037 BCCellBorder& operator [](int32_t i) const |
|
5038 { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error"); |
|
5039 return borders[clamped(i, startIndex, endIndex) - startIndex]; } |
|
5040 |
|
5041 int32_t startIndex; |
|
5042 int32_t endIndex; |
|
5043 BCCellBorder* borders; |
|
5044 }; |
|
5045 |
|
5046 BCCellBorders::BCCellBorders(int32_t aNumBorders, |
|
5047 int32_t aStartIndex) |
|
5048 { |
|
5049 NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error"); |
|
5050 startIndex = aStartIndex; |
|
5051 endIndex = aStartIndex + aNumBorders - 1; |
|
5052 borders = new BCCellBorder[aNumBorders]; |
|
5053 } |
|
5054 |
|
5055 // this function sets the new border properties and returns true if the border |
|
5056 // segment will start a new segment and not be accumulated into the previous |
|
5057 // segment. |
|
5058 static bool |
|
5059 SetBorder(const BCCellBorder& aNewBorder, |
|
5060 BCCellBorder& aBorder) |
|
5061 { |
|
5062 bool changed = (aNewBorder.style != aBorder.style) || |
|
5063 (aNewBorder.width != aBorder.width) || |
|
5064 (aNewBorder.color != aBorder.color); |
|
5065 aBorder.color = aNewBorder.color; |
|
5066 aBorder.width = aNewBorder.width; |
|
5067 aBorder.style = aNewBorder.style; |
|
5068 aBorder.owner = aNewBorder.owner; |
|
5069 |
|
5070 return changed; |
|
5071 } |
|
5072 |
|
5073 // this function will set the horizontal border. It will return true if the |
|
5074 // existing segment will not be continued. Having a vertical owner of a corner |
|
5075 // should also start a new segment. |
|
5076 static bool |
|
5077 SetHorBorder(const BCCellBorder& aNewBorder, |
|
5078 const BCCornerInfo& aCorner, |
|
5079 BCCellBorder& aBorder) |
|
5080 { |
|
5081 bool startSeg = ::SetBorder(aNewBorder, aBorder); |
|
5082 if (!startSeg) { |
|
5083 startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide)); |
|
5084 } |
|
5085 return startSeg; |
|
5086 } |
|
5087 |
|
5088 // Make the damage area larger on the top and bottom by at least one row and on the left and right |
|
5089 // at least one column. This is done so that adjacent elements are part of the border calculations. |
|
5090 // The extra segments and borders outside the actual damage area will not be updated in the cell map, |
|
5091 // because they in turn would need info from adjacent segments outside the damage area to be accurate. |
|
5092 void |
|
5093 nsTableFrame::ExpandBCDamageArea(nsIntRect& aRect) const |
|
5094 { |
|
5095 int32_t numRows = GetRowCount(); |
|
5096 int32_t numCols = GetColCount(); |
|
5097 |
|
5098 int32_t dStartX = aRect.x; |
|
5099 int32_t dEndX = aRect.XMost() - 1; |
|
5100 int32_t dStartY = aRect.y; |
|
5101 int32_t dEndY = aRect.YMost() - 1; |
|
5102 |
|
5103 // expand the damage area in each direction |
|
5104 if (dStartX > 0) { |
|
5105 dStartX--; |
|
5106 } |
|
5107 if (dEndX < (numCols - 1)) { |
|
5108 dEndX++; |
|
5109 } |
|
5110 if (dStartY > 0) { |
|
5111 dStartY--; |
|
5112 } |
|
5113 if (dEndY < (numRows - 1)) { |
|
5114 dEndY++; |
|
5115 } |
|
5116 // Check the damage area so that there are no cells spanning in or out. If there are any then |
|
5117 // make the damage area as big as the table, similarly to the way the cell map decides whether |
|
5118 // to rebuild versus expand. This could be optimized to expand to the smallest area that contains |
|
5119 // no spanners, but it may not be worth the effort in general, and it would need to be done in the |
|
5120 // cell map as well. |
|
5121 bool haveSpanner = false; |
|
5122 if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) { |
|
5123 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); |
|
5124 // Get the ordered row groups |
|
5125 RowGroupArray rowGroups; |
|
5126 OrderRowGroups(rowGroups); |
|
5127 |
|
5128 // Scope outside loop to be used as hint. |
|
5129 nsCellMap* cellMap = nullptr; |
|
5130 for (uint32_t rgX = 0; rgX < rowGroups.Length(); rgX++) { |
|
5131 nsTableRowGroupFrame* rgFrame = rowGroups[rgX]; |
|
5132 int32_t rgStartY = rgFrame->GetStartRowIndex(); |
|
5133 int32_t rgEndY = rgStartY + rgFrame->GetRowCount() - 1; |
|
5134 if (dEndY < rgStartY) |
|
5135 break; |
|
5136 cellMap = tableCellMap->GetMapFor(rgFrame, cellMap); |
|
5137 if (!cellMap) ABORT0(); |
|
5138 // check for spanners from above and below |
|
5139 if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) { |
|
5140 if (uint32_t(dStartY - rgStartY) >= cellMap->mRows.Length()) |
|
5141 ABORT0(); |
|
5142 const nsCellMap::CellDataArray& row = |
|
5143 cellMap->mRows[dStartY - rgStartY]; |
|
5144 for (int32_t x = dStartX; x <= dEndX; x++) { |
|
5145 CellData* cellData = row.SafeElementAt(x); |
|
5146 if (cellData && (cellData->IsRowSpan())) { |
|
5147 haveSpanner = true; |
|
5148 break; |
|
5149 } |
|
5150 } |
|
5151 if (dEndY < rgEndY) { |
|
5152 if (uint32_t(dEndY + 1 - rgStartY) >= cellMap->mRows.Length()) |
|
5153 ABORT0(); |
|
5154 const nsCellMap::CellDataArray& row2 = |
|
5155 cellMap->mRows[dEndY + 1 - rgStartY]; |
|
5156 for (int32_t x = dStartX; x <= dEndX; x++) { |
|
5157 CellData* cellData = row2.SafeElementAt(x); |
|
5158 if (cellData && (cellData->IsRowSpan())) { |
|
5159 haveSpanner = true; |
|
5160 break; |
|
5161 } |
|
5162 } |
|
5163 } |
|
5164 } |
|
5165 // check for spanners on the left and right |
|
5166 int32_t iterStartY = -1; |
|
5167 int32_t iterEndY = -1; |
|
5168 if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) { |
|
5169 // the damage area starts in the row group |
|
5170 iterStartY = dStartY; |
|
5171 iterEndY = std::min(dEndY, rgEndY); |
|
5172 } |
|
5173 else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) { |
|
5174 // the damage area ends in the row group |
|
5175 iterStartY = rgStartY; |
|
5176 iterEndY = dEndY; |
|
5177 } |
|
5178 else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) { |
|
5179 // the damage area contains the row group |
|
5180 iterStartY = rgStartY; |
|
5181 iterEndY = rgEndY; |
|
5182 } |
|
5183 if ((iterStartY >= 0) && (iterEndY >= 0)) { |
|
5184 for (int32_t y = iterStartY; y <= iterEndY; y++) { |
|
5185 if (uint32_t(y - rgStartY) >= cellMap->mRows.Length()) |
|
5186 ABORT0(); |
|
5187 const nsCellMap::CellDataArray& row = |
|
5188 cellMap->mRows[y - rgStartY]; |
|
5189 CellData* cellData = row.SafeElementAt(dStartX); |
|
5190 if (cellData && (cellData->IsColSpan())) { |
|
5191 haveSpanner = true; |
|
5192 break; |
|
5193 } |
|
5194 if (dEndX < (numCols - 1)) { |
|
5195 cellData = row.SafeElementAt(dEndX + 1); |
|
5196 if (cellData && (cellData->IsColSpan())) { |
|
5197 haveSpanner = true; |
|
5198 break; |
|
5199 } |
|
5200 } |
|
5201 } |
|
5202 } |
|
5203 } |
|
5204 } |
|
5205 if (haveSpanner) { |
|
5206 // make the damage area the whole table |
|
5207 aRect.x = 0; |
|
5208 aRect.y = 0; |
|
5209 aRect.width = numCols; |
|
5210 aRect.height = numRows; |
|
5211 } |
|
5212 else { |
|
5213 aRect.x = dStartX; |
|
5214 aRect.y = dStartY; |
|
5215 aRect.width = 1 + dEndX - dStartX; |
|
5216 aRect.height = 1 + dEndY - dStartY; |
|
5217 } |
|
5218 } |
|
5219 |
|
5220 |
|
5221 #define ADJACENT true |
|
5222 #define HORIZONTAL true |
|
5223 |
|
5224 void |
|
5225 BCMapCellInfo::SetTableTopLeftContBCBorder() |
|
5226 { |
|
5227 BCCellBorder currentBorder; |
|
5228 //calculate continuous top first row & rowgroup border: special case |
|
5229 //because it must include the table in the collapse |
|
5230 if (mTopRow) { |
|
5231 currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, |
|
5232 mTopRow, nullptr, mTableIsLTR, |
|
5233 NS_SIDE_TOP, !ADJACENT); |
|
5234 mTopRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); |
|
5235 } |
|
5236 if (mCgAtRight && mColGroup) { |
|
5237 //calculate continuous top colgroup border once per colgroup |
|
5238 currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, |
|
5239 mTopRow, nullptr, mTableIsLTR, |
|
5240 NS_SIDE_TOP, !ADJACENT); |
|
5241 mColGroup->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); |
|
5242 } |
|
5243 if (0 == mColIndex) { |
|
5244 currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, nullptr, |
|
5245 nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, |
|
5246 !ADJACENT); |
|
5247 mTableFrame->SetContinuousLeftBCBorderWidth(currentBorder.width); |
|
5248 } |
|
5249 } |
|
5250 |
|
5251 void |
|
5252 BCMapCellInfo::SetRowGroupLeftContBCBorder() |
|
5253 { |
|
5254 BCCellBorder currentBorder; |
|
5255 //get row group continuous borders |
|
5256 if (mRgAtBottom && mRowGroup) { //once per row group, so check for bottom |
|
5257 currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, |
|
5258 nullptr, nullptr, mTableIsLTR, NS_SIDE_LEFT, |
|
5259 !ADJACENT); |
|
5260 mRowGroup->SetContinuousBCBorderWidth(mStartSide, currentBorder.width); |
|
5261 } |
|
5262 } |
|
5263 |
|
5264 void |
|
5265 BCMapCellInfo::SetRowGroupRightContBCBorder() |
|
5266 { |
|
5267 BCCellBorder currentBorder; |
|
5268 //get row group continuous borders |
|
5269 if (mRgAtBottom && mRowGroup) { //once per mRowGroup, so check for bottom |
|
5270 currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, |
|
5271 nullptr, nullptr, mTableIsLTR, NS_SIDE_RIGHT, |
|
5272 ADJACENT); |
|
5273 mRowGroup->SetContinuousBCBorderWidth(mEndSide, currentBorder.width); |
|
5274 } |
|
5275 } |
|
5276 |
|
5277 void |
|
5278 BCMapCellInfo::SetColumnTopRightContBCBorder() |
|
5279 { |
|
5280 BCCellBorder currentBorder; |
|
5281 //calculate column continuous borders |
|
5282 //we only need to do this once, so we'll do it only on the first row |
|
5283 currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, |
|
5284 mCurrentColFrame, mRowGroup, mTopRow, nullptr, |
|
5285 mTableIsLTR, NS_SIDE_TOP, !ADJACENT); |
|
5286 ((nsTableColFrame*) mCurrentColFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP, |
|
5287 currentBorder.width); |
|
5288 if (mNumTableCols == GetCellEndColIndex() + 1) { |
|
5289 currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, |
|
5290 mCurrentColFrame, nullptr, nullptr, nullptr, |
|
5291 mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); |
|
5292 } |
|
5293 else { |
|
5294 currentBorder = CompareBorders(nullptr, mCurrentColGroupFrame, |
|
5295 mCurrentColFrame, nullptr,nullptr, nullptr, |
|
5296 mTableIsLTR, NS_SIDE_RIGHT, !ADJACENT); |
|
5297 } |
|
5298 mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_RIGHT, |
|
5299 currentBorder.width); |
|
5300 } |
|
5301 |
|
5302 void |
|
5303 BCMapCellInfo::SetColumnBottomContBCBorder() |
|
5304 { |
|
5305 BCCellBorder currentBorder; |
|
5306 //get col continuous border |
|
5307 currentBorder = CompareBorders(mTableFrame, mCurrentColGroupFrame, |
|
5308 mCurrentColFrame, mRowGroup, mBottomRow, |
|
5309 nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); |
|
5310 mCurrentColFrame->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, |
|
5311 currentBorder.width); |
|
5312 } |
|
5313 |
|
5314 void |
|
5315 BCMapCellInfo::SetColGroupBottomContBCBorder() |
|
5316 { |
|
5317 BCCellBorder currentBorder; |
|
5318 if (mColGroup) { |
|
5319 currentBorder = CompareBorders(mTableFrame, mColGroup, nullptr, mRowGroup, |
|
5320 mBottomRow, nullptr, mTableIsLTR, |
|
5321 NS_SIDE_BOTTOM, ADJACENT); |
|
5322 mColGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); |
|
5323 } |
|
5324 } |
|
5325 |
|
5326 void |
|
5327 BCMapCellInfo::SetRowGroupBottomContBCBorder() |
|
5328 { |
|
5329 BCCellBorder currentBorder; |
|
5330 if (mRowGroup) { |
|
5331 currentBorder = CompareBorders(mTableFrame, nullptr, nullptr, mRowGroup, |
|
5332 mBottomRow, nullptr, mTableIsLTR, |
|
5333 NS_SIDE_BOTTOM, ADJACENT); |
|
5334 mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); |
|
5335 } |
|
5336 } |
|
5337 |
|
5338 void |
|
5339 BCMapCellInfo::SetInnerRowGroupBottomContBCBorder(const nsIFrame* aNextRowGroup, |
|
5340 nsTableRowFrame* aNextRow) |
|
5341 { |
|
5342 BCCellBorder currentBorder, adjacentBorder; |
|
5343 |
|
5344 const nsIFrame* rowgroup = (mRgAtBottom) ? mRowGroup : nullptr; |
|
5345 currentBorder = CompareBorders(nullptr, nullptr, nullptr, rowgroup, mBottomRow, |
|
5346 nullptr, mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); |
|
5347 |
|
5348 adjacentBorder = CompareBorders(nullptr, nullptr, nullptr, aNextRowGroup, |
|
5349 aNextRow, nullptr, mTableIsLTR, NS_SIDE_TOP, |
|
5350 !ADJACENT); |
|
5351 currentBorder = CompareBorders(false, currentBorder, adjacentBorder, |
|
5352 HORIZONTAL); |
|
5353 if (aNextRow) { |
|
5354 aNextRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width); |
|
5355 } |
|
5356 if (mRgAtBottom && mRowGroup) { |
|
5357 mRowGroup->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width); |
|
5358 } |
|
5359 } |
|
5360 |
|
5361 void |
|
5362 BCMapCellInfo::SetRowLeftContBCBorder() |
|
5363 { |
|
5364 //get row continuous borders |
|
5365 if (mCurrentRowFrame) { |
|
5366 BCCellBorder currentBorder; |
|
5367 currentBorder = CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, |
|
5368 mCurrentRowFrame, nullptr, mTableIsLTR, |
|
5369 NS_SIDE_LEFT, !ADJACENT); |
|
5370 mCurrentRowFrame->SetContinuousBCBorderWidth(mStartSide, |
|
5371 currentBorder.width); |
|
5372 } |
|
5373 } |
|
5374 |
|
5375 void |
|
5376 BCMapCellInfo::SetRowRightContBCBorder() |
|
5377 { |
|
5378 if (mCurrentRowFrame) { |
|
5379 BCCellBorder currentBorder; |
|
5380 currentBorder = CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, |
|
5381 mCurrentRowFrame, nullptr, mTableIsLTR, |
|
5382 NS_SIDE_RIGHT, ADJACENT); |
|
5383 mCurrentRowFrame->SetContinuousBCBorderWidth(mEndSide, |
|
5384 currentBorder.width); |
|
5385 } |
|
5386 } |
|
5387 void |
|
5388 BCMapCellInfo::SetTableTopBorderWidth(BCPixelSize aWidth) |
|
5389 { |
|
5390 mTableBCData->mTopBorderWidth = std::max(mTableBCData->mTopBorderWidth, aWidth); |
|
5391 } |
|
5392 |
|
5393 void |
|
5394 BCMapCellInfo::SetTableLeftBorderWidth(int32_t aRowY, BCPixelSize aWidth) |
|
5395 { |
|
5396 // update the left/right first cell border |
|
5397 if (aRowY == 0) { |
|
5398 if (mTableIsLTR) { |
|
5399 mTableBCData->mLeftCellBorderWidth = aWidth; |
|
5400 } |
|
5401 else { |
|
5402 mTableBCData->mRightCellBorderWidth = aWidth; |
|
5403 } |
|
5404 } |
|
5405 mTableBCData->mLeftBorderWidth = std::max(mTableBCData->mLeftBorderWidth, |
|
5406 aWidth); |
|
5407 } |
|
5408 |
|
5409 void |
|
5410 BCMapCellInfo::SetTableRightBorderWidth(int32_t aRowY, BCPixelSize aWidth) |
|
5411 { |
|
5412 // update the left/right first cell border |
|
5413 if (aRowY == 0) { |
|
5414 if (mTableIsLTR) { |
|
5415 mTableBCData->mRightCellBorderWidth = aWidth; |
|
5416 } |
|
5417 else { |
|
5418 mTableBCData->mLeftCellBorderWidth = aWidth; |
|
5419 } |
|
5420 } |
|
5421 mTableBCData->mRightBorderWidth = std::max(mTableBCData->mRightBorderWidth, |
|
5422 aWidth); |
|
5423 } |
|
5424 |
|
5425 void |
|
5426 BCMapCellInfo::SetRightBorderWidths(BCPixelSize aWidth) |
|
5427 { |
|
5428 // update the borders of the cells and cols affected |
|
5429 if (mCell) { |
|
5430 mCell->SetBorderWidth(mEndSide, std::max(aWidth, |
|
5431 mCell->GetBorderWidth(mEndSide))); |
|
5432 } |
|
5433 if (mRightCol) { |
|
5434 BCPixelSize half = BC_BORDER_LEFT_HALF(aWidth); |
|
5435 mRightCol->SetRightBorderWidth(std::max(nscoord(half), |
|
5436 mRightCol->GetRightBorderWidth())); |
|
5437 } |
|
5438 } |
|
5439 |
|
5440 void |
|
5441 BCMapCellInfo::SetBottomBorderWidths(BCPixelSize aWidth) |
|
5442 { |
|
5443 // update the borders of the affected cells and rows |
|
5444 if (mCell) { |
|
5445 mCell->SetBorderWidth(NS_SIDE_BOTTOM, std::max(aWidth, |
|
5446 mCell->GetBorderWidth(NS_SIDE_BOTTOM))); |
|
5447 } |
|
5448 if (mBottomRow) { |
|
5449 BCPixelSize half = BC_BORDER_TOP_HALF(aWidth); |
|
5450 mBottomRow->SetBottomBCBorderWidth(std::max(nscoord(half), |
|
5451 mBottomRow->GetBottomBCBorderWidth())); |
|
5452 } |
|
5453 } |
|
5454 void |
|
5455 BCMapCellInfo::SetTopBorderWidths(BCPixelSize aWidth) |
|
5456 { |
|
5457 if (mCell) { |
|
5458 mCell->SetBorderWidth(NS_SIDE_TOP, std::max(aWidth, |
|
5459 mCell->GetBorderWidth(NS_SIDE_TOP))); |
|
5460 } |
|
5461 if (mTopRow) { |
|
5462 BCPixelSize half = BC_BORDER_BOTTOM_HALF(aWidth); |
|
5463 mTopRow->SetTopBCBorderWidth(std::max(nscoord(half), |
|
5464 mTopRow->GetTopBCBorderWidth())); |
|
5465 } |
|
5466 } |
|
5467 void |
|
5468 BCMapCellInfo::SetLeftBorderWidths(BCPixelSize aWidth) |
|
5469 { |
|
5470 if (mCell) { |
|
5471 mCell->SetBorderWidth(mStartSide, std::max(aWidth, |
|
5472 mCell->GetBorderWidth(mStartSide))); |
|
5473 } |
|
5474 if (mLeftCol) { |
|
5475 BCPixelSize half = BC_BORDER_RIGHT_HALF(aWidth); |
|
5476 mLeftCol->SetLeftBorderWidth(std::max(nscoord(half), |
|
5477 mLeftCol->GetLeftBorderWidth())); |
|
5478 } |
|
5479 } |
|
5480 |
|
5481 void |
|
5482 BCMapCellInfo::SetTableBottomBorderWidth(BCPixelSize aWidth) |
|
5483 { |
|
5484 mTableBCData->mBottomBorderWidth = std::max(mTableBCData->mBottomBorderWidth, |
|
5485 aWidth); |
|
5486 } |
|
5487 |
|
5488 void |
|
5489 BCMapCellInfo::SetColumn(int32_t aColX) |
|
5490 { |
|
5491 mCurrentColFrame = mTableFrame->GetColFrame(aColX); |
|
5492 if (!mCurrentColFrame) { |
|
5493 NS_ERROR("null mCurrentColFrame"); |
|
5494 } |
|
5495 mCurrentColGroupFrame = static_cast<nsTableColGroupFrame*> |
|
5496 (mCurrentColFrame->GetParent()); |
|
5497 if (!mCurrentColGroupFrame) { |
|
5498 NS_ERROR("null mCurrentColGroupFrame"); |
|
5499 } |
|
5500 } |
|
5501 |
|
5502 void |
|
5503 BCMapCellInfo::IncrementRow(bool aResetToTopRowOfCell) |
|
5504 { |
|
5505 mCurrentRowFrame = (aResetToTopRowOfCell) ? mTopRow : |
|
5506 mCurrentRowFrame->GetNextRow(); |
|
5507 } |
|
5508 |
|
5509 BCCellBorder |
|
5510 BCMapCellInfo::GetTopEdgeBorder() |
|
5511 { |
|
5512 return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, |
|
5513 mRowGroup, mTopRow, mCell, mTableIsLTR, NS_SIDE_TOP, |
|
5514 !ADJACENT); |
|
5515 } |
|
5516 |
|
5517 BCCellBorder |
|
5518 BCMapCellInfo::GetBottomEdgeBorder() |
|
5519 { |
|
5520 return CompareBorders(mTableFrame, mCurrentColGroupFrame, mCurrentColFrame, |
|
5521 mRowGroup, mBottomRow, mCell, mTableIsLTR, |
|
5522 NS_SIDE_BOTTOM, ADJACENT); |
|
5523 } |
|
5524 BCCellBorder |
|
5525 BCMapCellInfo::GetLeftEdgeBorder() |
|
5526 { |
|
5527 return CompareBorders(mTableFrame, mColGroup, mLeftCol, mRowGroup, |
|
5528 mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_LEFT, |
|
5529 !ADJACENT); |
|
5530 } |
|
5531 BCCellBorder |
|
5532 BCMapCellInfo::GetRightEdgeBorder() |
|
5533 { |
|
5534 return CompareBorders(mTableFrame, mColGroup, mRightCol, mRowGroup, |
|
5535 mCurrentRowFrame, mCell, mTableIsLTR, NS_SIDE_RIGHT, |
|
5536 ADJACENT); |
|
5537 } |
|
5538 BCCellBorder |
|
5539 BCMapCellInfo::GetRightInternalBorder() |
|
5540 { |
|
5541 const nsIFrame* cg = (mCgAtRight) ? mColGroup : nullptr; |
|
5542 return CompareBorders(nullptr, cg, mRightCol, nullptr, nullptr, mCell, |
|
5543 mTableIsLTR, NS_SIDE_RIGHT, ADJACENT); |
|
5544 } |
|
5545 |
|
5546 BCCellBorder |
|
5547 BCMapCellInfo::GetLeftInternalBorder() |
|
5548 { |
|
5549 const nsIFrame* cg = (mCgAtLeft) ? mColGroup : nullptr; |
|
5550 return CompareBorders(nullptr, cg, mLeftCol, nullptr, nullptr, mCell, |
|
5551 mTableIsLTR, NS_SIDE_LEFT, !ADJACENT); |
|
5552 } |
|
5553 |
|
5554 BCCellBorder |
|
5555 BCMapCellInfo::GetBottomInternalBorder() |
|
5556 { |
|
5557 const nsIFrame* rg = (mRgAtBottom) ? mRowGroup : nullptr; |
|
5558 return CompareBorders(nullptr, nullptr, nullptr, rg, mBottomRow, mCell, |
|
5559 mTableIsLTR, NS_SIDE_BOTTOM, ADJACENT); |
|
5560 } |
|
5561 |
|
5562 BCCellBorder |
|
5563 BCMapCellInfo::GetTopInternalBorder() |
|
5564 { |
|
5565 const nsIFrame* rg = (mRgAtTop) ? mRowGroup : nullptr; |
|
5566 return CompareBorders(nullptr, nullptr, nullptr, rg, mTopRow, mCell, |
|
5567 mTableIsLTR, NS_SIDE_TOP, !ADJACENT); |
|
5568 } |
|
5569 |
|
5570 /* Here is the order for storing border edges in the cell map as a cell is processed. There are |
|
5571 n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell. |
|
5572 |
|
5573 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since |
|
5574 a bottom edge from a cell above will take care of it. |
|
5575 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since |
|
5576 a right edge from a cell to the left will take care of it. |
|
5577 3) Store the right edge (or edges if a row span) |
|
5578 4) Store the bottom edge (or edges if a col span) |
|
5579 |
|
5580 Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner |
|
5581 calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries |
|
5582 would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing |
|
5583 borders already have about an extra 8 byte per cell map entry overhead (this could be |
|
5584 reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the |
|
5585 rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the |
|
5586 left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border |
|
5587 edges per cell and n=rowspan left and right border edges per cell. |
|
5588 |
|
5589 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table. |
|
5590 Never store the top-right corner, since it will get stored as a right-top corner. |
|
5591 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner, |
|
5592 since it will get stored as a bottom-left corner. |
|
5593 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on |
|
5594 the top edge of the table. Never store the right-bottom corner since it will get stored as a |
|
5595 bottom-right corner. |
|
5596 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it |
|
5597 otherwise, since it will get stored as either a right-top corner by a cell below or |
|
5598 a bottom-left corner from a cell to the right. |
|
5599 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits |
|
5600 the top side of a colspan in its interior. Never store the corner otherwise, since it will |
|
5601 get stored as a right-top corner by a cell from below. |
|
5602 |
|
5603 XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686. |
|
5604 In order to draw borders in rtl conditions somehow correct, the existing structure which relies |
|
5605 heavily on the assumption that the next cell sibling will be on the right side, has been modified. |
|
5606 We flip the border during painting and during style lookup. Look for tableIsLTR for places where |
|
5607 the flipping is done. |
|
5608 */ |
|
5609 |
|
5610 |
|
5611 |
|
5612 // Calc the dominant border at every cell edge and corner within the current damage area |
|
5613 void |
|
5614 nsTableFrame::CalcBCBorders() |
|
5615 { |
|
5616 NS_ASSERTION(IsBorderCollapse(), |
|
5617 "calling CalcBCBorders on separated-border table"); |
|
5618 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0(); |
|
5619 int32_t numRows = GetRowCount(); |
|
5620 int32_t numCols = GetColCount(); |
|
5621 if (!numRows || !numCols) |
|
5622 return; // nothing to do |
|
5623 |
|
5624 // Get the property holding the table damage area and border widths |
|
5625 BCPropertyData* propData = GetBCProperty(); |
|
5626 if (!propData) ABORT0(); |
|
5627 |
|
5628 |
|
5629 |
|
5630 // calculate an expanded damage area |
|
5631 nsIntRect damageArea(propData->mDamageArea); |
|
5632 ExpandBCDamageArea(damageArea); |
|
5633 |
|
5634 // segments that are on the table border edges need |
|
5635 // to be initialized only once |
|
5636 bool tableBorderReset[4]; |
|
5637 for (uint32_t sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) { |
|
5638 tableBorderReset[sideX] = false; |
|
5639 } |
|
5640 |
|
5641 // vertical borders indexed in x-direction (cols) |
|
5642 BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x); |
|
5643 if (!lastVerBorders.borders) ABORT0(); |
|
5644 BCCellBorder lastTopBorder, lastBottomBorder; |
|
5645 // horizontal borders indexed in x-direction (cols) |
|
5646 BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x); |
|
5647 if (!lastBottomBorders.borders) ABORT0(); |
|
5648 bool startSeg; |
|
5649 bool gotRowBorder = false; |
|
5650 |
|
5651 BCMapCellInfo info(this), ajaInfo(this); |
|
5652 |
|
5653 BCCellBorder currentBorder, adjacentBorder; |
|
5654 BCCorners topCorners(damageArea.width + 1, damageArea.x); |
|
5655 if (!topCorners.corners) ABORT0(); |
|
5656 BCCorners bottomCorners(damageArea.width + 1, damageArea.x); |
|
5657 if (!bottomCorners.corners) ABORT0(); |
|
5658 |
|
5659 BCMapCellIterator iter(this, damageArea); |
|
5660 for (iter.First(info); !iter.mAtEnd; iter.Next(info)) { |
|
5661 // see if lastTopBorder, lastBottomBorder need to be reset |
|
5662 if (iter.IsNewRow()) { |
|
5663 gotRowBorder = false; |
|
5664 lastTopBorder.Reset(info.mRowIndex, info.mRowSpan); |
|
5665 lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan); |
|
5666 } |
|
5667 else if (info.mColIndex > damageArea.x) { |
|
5668 lastBottomBorder = lastBottomBorders[info.mColIndex - 1]; |
|
5669 if (info.mRowIndex > |
|
5670 (lastBottomBorder.rowIndex - lastBottomBorder.rowSpan)) { |
|
5671 // the top border's left edge butts against the middle of a rowspan |
|
5672 lastTopBorder.Reset(info.mRowIndex, info.mRowSpan); |
|
5673 } |
|
5674 if (lastBottomBorder.rowIndex > (info.GetCellEndRowIndex() + 1)) { |
|
5675 // the bottom border's left edge butts against the middle of a rowspan |
|
5676 lastBottomBorder.Reset(info.GetCellEndRowIndex() + 1, info.mRowSpan); |
|
5677 } |
|
5678 } |
|
5679 |
|
5680 // find the dominant border considering the cell's top border and the table, |
|
5681 // row group, row if the border is at the top of the table, otherwise it was |
|
5682 // processed in a previous row |
|
5683 if (0 == info.mRowIndex) { |
|
5684 if (!tableBorderReset[NS_SIDE_TOP]) { |
|
5685 propData->mTopBorderWidth = 0; |
|
5686 tableBorderReset[NS_SIDE_TOP] = true; |
|
5687 } |
|
5688 for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); |
|
5689 colX++) { |
|
5690 info.SetColumn(colX); |
|
5691 currentBorder = info.GetTopEdgeBorder(); |
|
5692 // update/store the top left & top right corners of the seg |
|
5693 BCCornerInfo& tlCorner = topCorners[colX]; // top left |
|
5694 if (0 == colX) { |
|
5695 // we are on right hand side of the corner |
|
5696 tlCorner.Set(NS_SIDE_RIGHT, currentBorder); |
|
5697 } |
|
5698 else { |
|
5699 tlCorner.Update(NS_SIDE_RIGHT, currentBorder); |
|
5700 tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, 0, 0, colX, |
|
5701 mozilla::css::Side(tlCorner.ownerSide), |
|
5702 tlCorner.subWidth, |
|
5703 tlCorner.bevel); |
|
5704 } |
|
5705 topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right |
|
5706 // update lastTopBorder and see if a new segment starts |
|
5707 startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder); |
|
5708 // store the border segment in the cell map |
|
5709 tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *iter.mCellMap, 0, 0, colX, |
|
5710 1, currentBorder.owner, |
|
5711 currentBorder.width, startSeg); |
|
5712 |
|
5713 info.SetTableTopBorderWidth(currentBorder.width); |
|
5714 info.SetTopBorderWidths(currentBorder.width); |
|
5715 info.SetColumnTopRightContBCBorder(); |
|
5716 } |
|
5717 info.SetTableTopLeftContBCBorder(); |
|
5718 } |
|
5719 else { |
|
5720 // see if the top border needs to be the start of a segment due to a |
|
5721 // vertical border owning the corner |
|
5722 if (info.mColIndex > 0) { |
|
5723 BCData& data = info.mCellData->mData; |
|
5724 if (!data.IsTopStart()) { |
|
5725 mozilla::css::Side cornerSide; |
|
5726 bool bevel; |
|
5727 data.GetCorner(cornerSide, bevel); |
|
5728 if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) { |
|
5729 data.SetTopStart(true); |
|
5730 } |
|
5731 } |
|
5732 } |
|
5733 } |
|
5734 |
|
5735 // find the dominant border considering the cell's left border and the |
|
5736 // table, col group, col if the border is at the left of the table, |
|
5737 // otherwise it was processed in a previous col |
|
5738 if (0 == info.mColIndex) { |
|
5739 if (!tableBorderReset[NS_SIDE_LEFT]) { |
|
5740 propData->mLeftBorderWidth = 0; |
|
5741 tableBorderReset[NS_SIDE_LEFT] = true; |
|
5742 } |
|
5743 info.mCurrentRowFrame = nullptr; |
|
5744 for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); |
|
5745 rowY++) { |
|
5746 info.IncrementRow(rowY == info.mRowIndex); |
|
5747 currentBorder = info.GetLeftEdgeBorder(); |
|
5748 BCCornerInfo& tlCorner = (0 == rowY) ? topCorners[0] : bottomCorners[0]; |
|
5749 tlCorner.Update(NS_SIDE_BOTTOM, currentBorder); |
|
5750 tableCellMap->SetBCBorderCorner(eTopLeft, *iter.mCellMap, |
|
5751 iter.mRowGroupStart, rowY, 0, |
|
5752 mozilla::css::Side(tlCorner.ownerSide), |
|
5753 tlCorner.subWidth, |
|
5754 tlCorner.bevel); |
|
5755 bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left |
|
5756 |
|
5757 // update lastVerBordersBorder and see if a new segment starts |
|
5758 startSeg = SetBorder(currentBorder, lastVerBorders[0]); |
|
5759 // store the border segment in the cell map |
|
5760 tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *iter.mCellMap, |
|
5761 iter.mRowGroupStart, rowY, info.mColIndex, |
|
5762 1, currentBorder.owner, |
|
5763 currentBorder.width, startSeg); |
|
5764 info.SetTableLeftBorderWidth(rowY , currentBorder.width); |
|
5765 info.SetLeftBorderWidths(currentBorder.width); |
|
5766 info.SetRowLeftContBCBorder(); |
|
5767 } |
|
5768 info.SetRowGroupLeftContBCBorder(); |
|
5769 } |
|
5770 |
|
5771 // find the dominant border considering the cell's right border, adjacent |
|
5772 // cells and the table, row group, row |
|
5773 if (info.mNumTableCols == info.GetCellEndColIndex() + 1) { |
|
5774 // touches right edge of table |
|
5775 if (!tableBorderReset[NS_SIDE_RIGHT]) { |
|
5776 propData->mRightBorderWidth = 0; |
|
5777 tableBorderReset[NS_SIDE_RIGHT] = true; |
|
5778 } |
|
5779 info.mCurrentRowFrame = nullptr; |
|
5780 for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); |
|
5781 rowY++) { |
|
5782 info.IncrementRow(rowY == info.mRowIndex); |
|
5783 currentBorder = info.GetRightEdgeBorder(); |
|
5784 // update/store the top right & bottom right corners |
|
5785 BCCornerInfo& trCorner = (0 == rowY) ? |
|
5786 topCorners[info.GetCellEndColIndex() + 1] : |
|
5787 bottomCorners[info.GetCellEndColIndex() + 1]; |
|
5788 trCorner.Update(NS_SIDE_BOTTOM, currentBorder); // top right |
|
5789 tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap, |
|
5790 iter.mRowGroupStart, rowY, |
|
5791 info.GetCellEndColIndex(), |
|
5792 mozilla::css::Side(trCorner.ownerSide), |
|
5793 trCorner.subWidth, |
|
5794 trCorner.bevel); |
|
5795 BCCornerInfo& brCorner = bottomCorners[info.GetCellEndColIndex() + 1]; |
|
5796 brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right |
|
5797 tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, |
|
5798 iter.mRowGroupStart, rowY, |
|
5799 info.GetCellEndColIndex(), |
|
5800 mozilla::css::Side(brCorner.ownerSide), |
|
5801 brCorner.subWidth, |
|
5802 brCorner.bevel); |
|
5803 // update lastVerBorders and see if a new segment starts |
|
5804 startSeg = SetBorder(currentBorder, |
|
5805 lastVerBorders[info.GetCellEndColIndex() + 1]); |
|
5806 // store the border segment in the cell map and update cellBorders |
|
5807 tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap, |
|
5808 iter.mRowGroupStart, rowY, |
|
5809 info.GetCellEndColIndex(), 1, |
|
5810 currentBorder.owner, currentBorder.width, |
|
5811 startSeg); |
|
5812 info.SetTableRightBorderWidth(rowY, currentBorder.width); |
|
5813 info.SetRightBorderWidths(currentBorder.width); |
|
5814 info.SetRowRightContBCBorder(); |
|
5815 } |
|
5816 info.SetRowGroupRightContBCBorder(); |
|
5817 } |
|
5818 else { |
|
5819 int32_t segLength = 0; |
|
5820 BCMapCellInfo priorAjaInfo(this); |
|
5821 for (int32_t rowY = info.mRowIndex; rowY <= info.GetCellEndRowIndex(); |
|
5822 rowY += segLength) { |
|
5823 iter.PeekRight(info, rowY, ajaInfo); |
|
5824 currentBorder = info.GetRightInternalBorder(); |
|
5825 adjacentBorder = ajaInfo.GetLeftInternalBorder(); |
|
5826 currentBorder = CompareBorders(!CELL_CORNER, currentBorder, |
|
5827 adjacentBorder, !HORIZONTAL); |
|
5828 |
|
5829 segLength = std::max(1, ajaInfo.mRowIndex + ajaInfo.mRowSpan - rowY); |
|
5830 segLength = std::min(segLength, info.mRowIndex + info.mRowSpan - rowY); |
|
5831 |
|
5832 // update lastVerBorders and see if a new segment starts |
|
5833 startSeg = SetBorder(currentBorder, |
|
5834 lastVerBorders[info.GetCellEndColIndex() + 1]); |
|
5835 // store the border segment in the cell map and update cellBorders |
|
5836 if (info.GetCellEndColIndex() < damageArea.XMost() && |
|
5837 rowY >= damageArea.y && rowY < damageArea.YMost()) { |
|
5838 tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *iter.mCellMap, |
|
5839 iter.mRowGroupStart, rowY, |
|
5840 info.GetCellEndColIndex(), segLength, |
|
5841 currentBorder.owner, |
|
5842 currentBorder.width, startSeg); |
|
5843 info.SetRightBorderWidths(currentBorder.width); |
|
5844 ajaInfo.SetLeftBorderWidths(currentBorder.width); |
|
5845 } |
|
5846 // update the top right corner |
|
5847 bool hitsSpanOnRight = (rowY > ajaInfo.mRowIndex) && |
|
5848 (rowY < ajaInfo.mRowIndex + ajaInfo.mRowSpan); |
|
5849 BCCornerInfo* trCorner = ((0 == rowY) || hitsSpanOnRight) ? |
|
5850 &topCorners[info.GetCellEndColIndex() + 1] : |
|
5851 &bottomCorners[info.GetCellEndColIndex() + 1]; |
|
5852 trCorner->Update(NS_SIDE_BOTTOM, currentBorder); |
|
5853 // if this is not the first time through, |
|
5854 // consider the segment to the right |
|
5855 if (rowY != info.mRowIndex) { |
|
5856 currentBorder = priorAjaInfo.GetBottomInternalBorder(); |
|
5857 adjacentBorder = ajaInfo.GetTopInternalBorder(); |
|
5858 currentBorder = CompareBorders(!CELL_CORNER, currentBorder, |
|
5859 adjacentBorder, HORIZONTAL); |
|
5860 trCorner->Update(NS_SIDE_RIGHT, currentBorder); |
|
5861 } |
|
5862 // store the top right corner in the cell map |
|
5863 if (info.GetCellEndColIndex() < damageArea.XMost() && |
|
5864 rowY >= damageArea.y) { |
|
5865 if (0 != rowY) { |
|
5866 tableCellMap->SetBCBorderCorner(eTopRight, *iter.mCellMap, |
|
5867 iter.mRowGroupStart, rowY, |
|
5868 info.GetCellEndColIndex(), |
|
5869 mozilla::css::Side(trCorner->ownerSide), |
|
5870 trCorner->subWidth, |
|
5871 trCorner->bevel); |
|
5872 } |
|
5873 // store any corners this cell spans together with the aja cell |
|
5874 for (int32_t rX = rowY + 1; rX < rowY + segLength; rX++) { |
|
5875 tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, |
|
5876 iter.mRowGroupStart, rX, |
|
5877 info.GetCellEndColIndex(), |
|
5878 mozilla::css::Side(trCorner->ownerSide), |
|
5879 trCorner->subWidth, false); |
|
5880 } |
|
5881 } |
|
5882 // update bottom right corner, topCorners, bottomCorners |
|
5883 hitsSpanOnRight = (rowY + segLength < |
|
5884 ajaInfo.mRowIndex + ajaInfo.mRowSpan); |
|
5885 BCCornerInfo& brCorner = (hitsSpanOnRight) ? |
|
5886 topCorners[info.GetCellEndColIndex() + 1] : |
|
5887 bottomCorners[info.GetCellEndColIndex() + 1]; |
|
5888 brCorner.Set(NS_SIDE_TOP, currentBorder); |
|
5889 priorAjaInfo = ajaInfo; |
|
5890 } |
|
5891 } |
|
5892 for (int32_t colX = info.mColIndex + 1; colX <= info.GetCellEndColIndex(); |
|
5893 colX++) { |
|
5894 lastVerBorders[colX].Reset(0,1); |
|
5895 } |
|
5896 |
|
5897 // find the dominant border considering the cell's bottom border, adjacent |
|
5898 // cells and the table, row group, row |
|
5899 if (info.mNumTableRows == info.GetCellEndRowIndex() + 1) { |
|
5900 // touches bottom edge of table |
|
5901 if (!tableBorderReset[NS_SIDE_BOTTOM]) { |
|
5902 propData->mBottomBorderWidth = 0; |
|
5903 tableBorderReset[NS_SIDE_BOTTOM] = true; |
|
5904 } |
|
5905 for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); |
|
5906 colX++) { |
|
5907 info.SetColumn(colX); |
|
5908 currentBorder = info.GetBottomEdgeBorder(); |
|
5909 // update/store the bottom left & bottom right corners |
|
5910 BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left |
|
5911 blCorner.Update(NS_SIDE_RIGHT, currentBorder); |
|
5912 tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, |
|
5913 iter.mRowGroupStart, |
|
5914 info.GetCellEndRowIndex(), |
|
5915 colX, |
|
5916 mozilla::css::Side(blCorner.ownerSide), |
|
5917 blCorner.subWidth, blCorner.bevel); |
|
5918 BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right |
|
5919 brCorner.Update(NS_SIDE_LEFT, currentBorder); |
|
5920 if (info.mNumTableCols == colX + 1) { // lower right corner of the table |
|
5921 tableCellMap->SetBCBorderCorner(eBottomRight, *iter.mCellMap, |
|
5922 iter.mRowGroupStart, |
|
5923 info.GetCellEndRowIndex(),colX, |
|
5924 mozilla::css::Side(brCorner.ownerSide), |
|
5925 brCorner.subWidth, |
|
5926 brCorner.bevel, true); |
|
5927 } |
|
5928 // update lastBottomBorder and see if a new segment starts |
|
5929 startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder); |
|
5930 if (!startSeg) { |
|
5931 // make sure that we did not compare apples to oranges i.e. the |
|
5932 // current border should be a continuation of the lastBottomBorder, |
|
5933 // as it is a bottom border |
|
5934 // add 1 to the info.GetCellEndRowIndex() |
|
5935 startSeg = (lastBottomBorder.rowIndex != |
|
5936 (info.GetCellEndRowIndex() + 1)); |
|
5937 } |
|
5938 // store the border segment in the cell map and update cellBorders |
|
5939 tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap, |
|
5940 iter.mRowGroupStart, |
|
5941 info.GetCellEndRowIndex(), |
|
5942 colX, 1, currentBorder.owner, |
|
5943 currentBorder.width, startSeg); |
|
5944 // update lastBottomBorders |
|
5945 lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1; |
|
5946 lastBottomBorder.rowSpan = info.mRowSpan; |
|
5947 lastBottomBorders[colX] = lastBottomBorder; |
|
5948 |
|
5949 info.SetBottomBorderWidths(currentBorder.width); |
|
5950 info.SetTableBottomBorderWidth(currentBorder.width); |
|
5951 info.SetColumnBottomContBCBorder(); |
|
5952 } |
|
5953 info.SetRowGroupBottomContBCBorder(); |
|
5954 info.SetColGroupBottomContBCBorder(); |
|
5955 } |
|
5956 else { |
|
5957 int32_t segLength = 0; |
|
5958 for (int32_t colX = info.mColIndex; colX <= info.GetCellEndColIndex(); |
|
5959 colX += segLength) { |
|
5960 iter.PeekBottom(info, colX, ajaInfo); |
|
5961 currentBorder = info.GetBottomInternalBorder(); |
|
5962 adjacentBorder = ajaInfo.GetTopInternalBorder(); |
|
5963 currentBorder = CompareBorders(!CELL_CORNER, currentBorder, |
|
5964 adjacentBorder, HORIZONTAL); |
|
5965 segLength = std::max(1, ajaInfo.mColIndex + ajaInfo.mColSpan - colX); |
|
5966 segLength = std::min(segLength, info.mColIndex + info.mColSpan - colX); |
|
5967 |
|
5968 // update, store the bottom left corner |
|
5969 BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left |
|
5970 bool hitsSpanBelow = (colX > ajaInfo.mColIndex) && |
|
5971 (colX < ajaInfo.mColIndex + ajaInfo.mColSpan); |
|
5972 bool update = true; |
|
5973 if ((colX == info.mColIndex) && (colX > damageArea.x)) { |
|
5974 int32_t prevRowIndex = lastBottomBorders[colX - 1].rowIndex; |
|
5975 if (prevRowIndex > info.GetCellEndRowIndex() + 1) { |
|
5976 // hits a rowspan on the right |
|
5977 update = false; |
|
5978 // the corner was taken care of during the cell on the left |
|
5979 } |
|
5980 else if (prevRowIndex < info.GetCellEndRowIndex() + 1) { |
|
5981 // spans below the cell to the left |
|
5982 topCorners[colX] = blCorner; |
|
5983 blCorner.Set(NS_SIDE_RIGHT, currentBorder); |
|
5984 update = false; |
|
5985 } |
|
5986 } |
|
5987 if (update) { |
|
5988 blCorner.Update(NS_SIDE_RIGHT, currentBorder); |
|
5989 } |
|
5990 if (info.GetCellEndRowIndex() < damageArea.YMost() && |
|
5991 (colX >= damageArea.x)) { |
|
5992 if (hitsSpanBelow) { |
|
5993 tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, |
|
5994 iter.mRowGroupStart, |
|
5995 info.GetCellEndRowIndex(), colX, |
|
5996 mozilla::css::Side(blCorner.ownerSide), |
|
5997 blCorner.subWidth, blCorner.bevel); |
|
5998 } |
|
5999 // store any corners this cell spans together with the aja cell |
|
6000 for (int32_t cX = colX + 1; cX < colX + segLength; cX++) { |
|
6001 BCCornerInfo& corner = bottomCorners[cX]; |
|
6002 corner.Set(NS_SIDE_RIGHT, currentBorder); |
|
6003 tableCellMap->SetBCBorderCorner(eBottomLeft, *iter.mCellMap, |
|
6004 iter.mRowGroupStart, |
|
6005 info.GetCellEndRowIndex(), cX, |
|
6006 mozilla::css::Side(corner.ownerSide), |
|
6007 corner.subWidth, |
|
6008 false); |
|
6009 } |
|
6010 } |
|
6011 // update lastBottomBorders and see if a new segment starts |
|
6012 startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder); |
|
6013 if (!startSeg) { |
|
6014 // make sure that we did not compare apples to oranges i.e. the |
|
6015 // current border should be a continuation of the lastBottomBorder, |
|
6016 // as it is a bottom border |
|
6017 // add 1 to the info.GetCellEndRowIndex() |
|
6018 startSeg = (lastBottomBorder.rowIndex != |
|
6019 info.GetCellEndRowIndex() + 1); |
|
6020 } |
|
6021 lastBottomBorder.rowIndex = info.GetCellEndRowIndex() + 1; |
|
6022 lastBottomBorder.rowSpan = info.mRowSpan; |
|
6023 for (int32_t cX = colX; cX < colX + segLength; cX++) { |
|
6024 lastBottomBorders[cX] = lastBottomBorder; |
|
6025 } |
|
6026 |
|
6027 // store the border segment the cell map and update cellBorders |
|
6028 if (info.GetCellEndRowIndex() < damageArea.YMost() && |
|
6029 (colX >= damageArea.x) && |
|
6030 (colX < damageArea.XMost())) { |
|
6031 tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *iter.mCellMap, |
|
6032 iter.mRowGroupStart, |
|
6033 info.GetCellEndRowIndex(), |
|
6034 colX, segLength, currentBorder.owner, |
|
6035 currentBorder.width, startSeg); |
|
6036 info.SetBottomBorderWidths(currentBorder.width); |
|
6037 ajaInfo.SetTopBorderWidths(currentBorder.width); |
|
6038 } |
|
6039 // update bottom right corner |
|
6040 BCCornerInfo& brCorner = bottomCorners[colX + segLength]; |
|
6041 brCorner.Update(NS_SIDE_LEFT, currentBorder); |
|
6042 } |
|
6043 if (!gotRowBorder && 1 == info.mRowSpan && |
|
6044 (ajaInfo.mTopRow || info.mRgAtBottom)) { |
|
6045 //get continuous row/row group border |
|
6046 //we need to check the row group's bottom border if this is |
|
6047 //the last row in the row group, but only a cell with rowspan=1 |
|
6048 //will know whether *this* row is at the bottom |
|
6049 const nsIFrame* nextRowGroup = (ajaInfo.mRgAtTop) ? ajaInfo.mRowGroup : |
|
6050 nullptr; |
|
6051 info.SetInnerRowGroupBottomContBCBorder(nextRowGroup, ajaInfo.mTopRow); |
|
6052 gotRowBorder = true; |
|
6053 } |
|
6054 } |
|
6055 |
|
6056 // see if the cell to the right had a rowspan and its lower left border |
|
6057 // needs be joined with this one's bottom |
|
6058 // if there is a cell to the right and the cell to right was a rowspan |
|
6059 if ((info.mNumTableCols != info.GetCellEndColIndex() + 1) && |
|
6060 (lastBottomBorders[info.GetCellEndColIndex() + 1].rowSpan > 1)) { |
|
6061 BCCornerInfo& corner = bottomCorners[info.GetCellEndColIndex() + 1]; |
|
6062 if ((NS_SIDE_TOP != corner.ownerSide) && |
|
6063 (NS_SIDE_BOTTOM != corner.ownerSide)) { |
|
6064 // not a vertical owner |
|
6065 BCCellBorder& thisBorder = lastBottomBorder; |
|
6066 BCCellBorder& nextBorder = lastBottomBorders[info.mColIndex + 1]; |
|
6067 if ((thisBorder.color == nextBorder.color) && |
|
6068 (thisBorder.width == nextBorder.width) && |
|
6069 (thisBorder.style == nextBorder.style)) { |
|
6070 // set the flag on the next border indicating it is not the start of a |
|
6071 // new segment |
|
6072 if (iter.mCellMap) { |
|
6073 tableCellMap->ResetTopStart(NS_SIDE_BOTTOM, *iter.mCellMap, |
|
6074 info.GetCellEndRowIndex(), |
|
6075 info.GetCellEndColIndex() + 1); |
|
6076 } |
|
6077 } |
|
6078 } |
|
6079 } |
|
6080 } // for (iter.First(info); info.mCell; iter.Next(info)) { |
|
6081 // reset the bc flag and damage area |
|
6082 SetNeedToCalcBCBorders(false); |
|
6083 propData->mDamageArea = nsIntRect(0,0,0,0); |
|
6084 #ifdef DEBUG_TABLE_CELLMAP |
|
6085 mCellMap->Dump(); |
|
6086 #endif |
|
6087 } |
|
6088 |
|
6089 class BCPaintBorderIterator; |
|
6090 |
|
6091 struct BCVerticalSeg |
|
6092 { |
|
6093 BCVerticalSeg(); |
|
6094 |
|
6095 void Start(BCPaintBorderIterator& aIter, |
|
6096 BCBorderOwner aBorderOwner, |
|
6097 BCPixelSize aVerSegWidth, |
|
6098 BCPixelSize aHorSegHeight); |
|
6099 |
|
6100 void Initialize(BCPaintBorderIterator& aIter); |
|
6101 void GetBottomCorner(BCPaintBorderIterator& aIter, |
|
6102 BCPixelSize aHorSegHeight); |
|
6103 |
|
6104 |
|
6105 void Paint(BCPaintBorderIterator& aIter, |
|
6106 nsRenderingContext& aRenderingContext, |
|
6107 BCPixelSize aHorSegHeight); |
|
6108 void AdvanceOffsetY(); |
|
6109 void IncludeCurrentBorder(BCPaintBorderIterator& aIter); |
|
6110 |
|
6111 |
|
6112 union { |
|
6113 nsTableColFrame* mCol; |
|
6114 int32_t mColWidth; |
|
6115 }; |
|
6116 nscoord mOffsetX; // x-offset with respect to the table edge |
|
6117 nscoord mOffsetY; // y-offset with respect to the table edge |
|
6118 nscoord mLength; // vertical length including corners |
|
6119 BCPixelSize mWidth; // width in pixels |
|
6120 |
|
6121 nsTableCellFrame* mAjaCell; // previous sibling to the first cell |
|
6122 // where the segment starts, it can be |
|
6123 // the owner of a segment |
|
6124 nsTableCellFrame* mFirstCell; // cell at the start of the segment |
|
6125 nsTableRowGroupFrame* mFirstRowGroup; // row group at the start of the segment |
|
6126 nsTableRowFrame* mFirstRow; // row at the start of the segment |
|
6127 nsTableCellFrame* mLastCell; // cell at the current end of the |
|
6128 // segment |
|
6129 |
|
6130 |
|
6131 uint8_t mOwner; // owner of the border, defines the |
|
6132 // style |
|
6133 mozilla::css::Side mTopBevelSide; // direction to bevel at the top |
|
6134 nscoord mTopBevelOffset; // how much to bevel at the top |
|
6135 BCPixelSize mBottomHorSegHeight; // height of the crossing |
|
6136 //horizontal border |
|
6137 nscoord mBottomOffset; // how much longer is the segment due |
|
6138 // to the horizontal border, by this |
|
6139 // amount the next segment needs to be |
|
6140 // shifted. |
|
6141 bool mIsBottomBevel; // should we bevel at the bottom |
|
6142 }; |
|
6143 |
|
6144 struct BCHorizontalSeg |
|
6145 { |
|
6146 BCHorizontalSeg(); |
|
6147 |
|
6148 void Start(BCPaintBorderIterator& aIter, |
|
6149 BCBorderOwner aBorderOwner, |
|
6150 BCPixelSize aBottomVerSegWidth, |
|
6151 BCPixelSize aHorSegHeight); |
|
6152 void GetRightCorner(BCPaintBorderIterator& aIter, |
|
6153 BCPixelSize aLeftSegWidth); |
|
6154 void AdvanceOffsetX(int32_t aIncrement); |
|
6155 void IncludeCurrentBorder(BCPaintBorderIterator& aIter); |
|
6156 void Paint(BCPaintBorderIterator& aIter, |
|
6157 nsRenderingContext& aRenderingContext); |
|
6158 |
|
6159 nscoord mOffsetX; // x-offset with respect to the table edge |
|
6160 nscoord mOffsetY; // y-offset with respect to the table edge |
|
6161 nscoord mLength; // horizontal length including corners |
|
6162 BCPixelSize mWidth; // border width in pixels |
|
6163 nscoord mLeftBevelOffset; // how much to bevel at the left |
|
6164 mozilla::css::Side mLeftBevelSide; // direction to bevel at the left |
|
6165 bool mIsRightBevel; // should we bevel at the right end |
|
6166 nscoord mRightBevelOffset; // how much to bevel at the right |
|
6167 mozilla::css::Side mRightBevelSide; // direction to bevel at the right |
|
6168 nscoord mEndOffset; // how much longer is the segment due |
|
6169 // to the vertical border, by this |
|
6170 // amount the next segment needs to be |
|
6171 // shifted. |
|
6172 uint8_t mOwner; // owner of the border, defines the |
|
6173 // style |
|
6174 nsTableCellFrame* mFirstCell; // cell at the start of the segment |
|
6175 nsTableCellFrame* mAjaCell; // neighboring cell to the first cell |
|
6176 // where the segment starts, it can be |
|
6177 // the owner of a segment |
|
6178 }; |
|
6179 |
|
6180 // Iterates over borders (left border, corner, top border) in the cell map within a damage area |
|
6181 // from left to right, top to bottom. All members are in terms of the 1st in flow frames, except |
|
6182 // where suffixed by InFlow. |
|
6183 class BCPaintBorderIterator |
|
6184 { |
|
6185 public: |
|
6186 |
|
6187 |
|
6188 BCPaintBorderIterator(nsTableFrame* aTable); |
|
6189 ~BCPaintBorderIterator() { if (mVerInfo) { |
|
6190 delete [] mVerInfo; |
|
6191 }} |
|
6192 void Reset(); |
|
6193 |
|
6194 /** |
|
6195 * Determine the damage area in terms of rows and columns and finalize |
|
6196 * mInitialOffsetX and mInitialOffsetY. |
|
6197 * @param aDirtyRect - dirty rect in table coordinates |
|
6198 * @return - true if we need to paint something given dirty rect |
|
6199 */ |
|
6200 bool SetDamageArea(const nsRect& aDamageRect); |
|
6201 void First(); |
|
6202 void Next(); |
|
6203 void AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext); |
|
6204 void AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext); |
|
6205 void ResetVerInfo(); |
|
6206 void StoreColumnWidth(int32_t aIndex); |
|
6207 bool VerticalSegmentOwnsCorner(); |
|
6208 |
|
6209 nsTableFrame* mTable; |
|
6210 nsTableFrame* mTableFirstInFlow; |
|
6211 nsTableCellMap* mTableCellMap; |
|
6212 nsCellMap* mCellMap; |
|
6213 bool mTableIsLTR; |
|
6214 int32_t mColInc; // +1 for ltr -1 for rtl |
|
6215 const nsStyleBackground* mTableBgColor; |
|
6216 nsTableFrame::RowGroupArray mRowGroups; |
|
6217 |
|
6218 nsTableRowGroupFrame* mPrevRg; |
|
6219 nsTableRowGroupFrame* mRg; |
|
6220 bool mIsRepeatedHeader; |
|
6221 bool mIsRepeatedFooter; |
|
6222 nsTableRowGroupFrame* mStartRg; // first row group in the damagearea |
|
6223 int32_t mRgIndex; // current row group index in the |
|
6224 // mRowgroups array |
|
6225 int32_t mFifRgFirstRowIndex; // start row index of the first in |
|
6226 // flow of the row group |
|
6227 int32_t mRgFirstRowIndex; // row index of the first row in the |
|
6228 // row group |
|
6229 int32_t mRgLastRowIndex; // row index of the last row in the row |
|
6230 // group |
|
6231 int32_t mNumTableRows; // number of rows in the table and all |
|
6232 // continuations |
|
6233 int32_t mNumTableCols; // number of columns in the table |
|
6234 int32_t mColIndex; // with respect to the table |
|
6235 int32_t mRowIndex; // with respect to the table |
|
6236 int32_t mRepeatedHeaderRowIndex; // row index in a repeated |
|
6237 //header, it's equivalent to |
|
6238 // mRowIndex when we're in a repeated |
|
6239 // header, and set to the last row |
|
6240 // index of a repeated header when |
|
6241 // we're not |
|
6242 bool mIsNewRow; |
|
6243 bool mAtEnd; // the iterator cycled over all |
|
6244 // borders |
|
6245 nsTableRowFrame* mPrevRow; |
|
6246 nsTableRowFrame* mRow; |
|
6247 nsTableRowFrame* mStartRow; //first row in a inside the damagearea |
|
6248 |
|
6249 |
|
6250 // cell properties |
|
6251 nsTableCellFrame* mPrevCell; |
|
6252 nsTableCellFrame* mCell; |
|
6253 BCCellData* mPrevCellData; |
|
6254 BCCellData* mCellData; |
|
6255 BCData* mBCData; |
|
6256 |
|
6257 bool IsTableTopMost() {return (mRowIndex == 0) && !mTable->GetPrevInFlow();} |
|
6258 bool IsTableRightMost() {return (mColIndex >= mNumTableCols);} |
|
6259 bool IsTableBottomMost() {return (mRowIndex >= mNumTableRows) && !mTable->GetNextInFlow();} |
|
6260 bool IsTableLeftMost() {return (mColIndex == 0);} |
|
6261 bool IsDamageAreaTopMost() {return (mRowIndex == mDamageArea.y);} |
|
6262 bool IsDamageAreaRightMost() {return (mColIndex >= mDamageArea.XMost());} |
|
6263 bool IsDamageAreaBottomMost() {return (mRowIndex >= mDamageArea.YMost());} |
|
6264 bool IsDamageAreaLeftMost() {return (mColIndex == mDamageArea.x);} |
|
6265 int32_t GetRelativeColIndex() {return (mColIndex - mDamageArea.x);} |
|
6266 |
|
6267 nsIntRect mDamageArea; // damageArea in cellmap coordinates |
|
6268 bool IsAfterRepeatedHeader() { return !mIsRepeatedHeader && (mRowIndex == (mRepeatedHeaderRowIndex + 1));} |
|
6269 bool StartRepeatedFooter() {return mIsRepeatedFooter && (mRowIndex == mRgFirstRowIndex) && (mRowIndex != mDamageArea.y);} |
|
6270 nscoord mInitialOffsetX; // offsetX of the first border with |
|
6271 // respect to the table |
|
6272 nscoord mInitialOffsetY; // offsetY of the first border with |
|
6273 // respect to the table |
|
6274 nscoord mNextOffsetY; // offsetY of the next segment |
|
6275 BCVerticalSeg* mVerInfo; // this array is used differently when |
|
6276 // horizontal and vertical borders are drawn |
|
6277 // When horizontal border are drawn we cache |
|
6278 // the column widths and the width of the |
|
6279 // vertical borders that arrive from top |
|
6280 // When we draw vertical borders we store |
|
6281 // lengths and width for vertical borders |
|
6282 // before they are drawn while we move over |
|
6283 // the columns in the damage area |
|
6284 // It has one more elements than columns are |
|
6285 //in the table. |
|
6286 BCHorizontalSeg mHorSeg; // the horizontal segment while we |
|
6287 // move over the colums |
|
6288 BCPixelSize mPrevHorSegHeight; // the height of the previous |
|
6289 // horizontal border |
|
6290 |
|
6291 private: |
|
6292 |
|
6293 bool SetNewRow(nsTableRowFrame* aRow = nullptr); |
|
6294 bool SetNewRowGroup(); |
|
6295 void SetNewData(int32_t aRowIndex, int32_t aColIndex); |
|
6296 |
|
6297 }; |
|
6298 |
|
6299 |
|
6300 |
|
6301 BCPaintBorderIterator::BCPaintBorderIterator(nsTableFrame* aTable) |
|
6302 { |
|
6303 mTable = aTable; |
|
6304 mVerInfo = nullptr; |
|
6305 nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); |
|
6306 mTableFirstInFlow = static_cast<nsTableFrame*>(mTable->FirstInFlow()); |
|
6307 mTableCellMap = mTable->GetCellMap(); |
|
6308 // y position of first row in damage area |
|
6309 mInitialOffsetY = mTable->GetPrevInFlow() ? 0 : childAreaOffset.top; |
|
6310 mNumTableRows = mTable->GetRowCount(); |
|
6311 mNumTableCols = mTable->GetColCount(); |
|
6312 |
|
6313 // Get the ordered row groups |
|
6314 mTable->OrderRowGroups(mRowGroups); |
|
6315 // initialize to a non existing index |
|
6316 mRepeatedHeaderRowIndex = -99; |
|
6317 |
|
6318 mTableIsLTR = mTable->StyleVisibility()->mDirection == |
|
6319 NS_STYLE_DIRECTION_LTR; |
|
6320 mColInc = (mTableIsLTR) ? 1 : -1; |
|
6321 |
|
6322 nsIFrame* bgFrame = |
|
6323 nsCSSRendering::FindNonTransparentBackgroundFrame(aTable); |
|
6324 mTableBgColor = bgFrame->StyleBackground(); |
|
6325 } |
|
6326 |
|
6327 bool |
|
6328 BCPaintBorderIterator::SetDamageArea(const nsRect& aDirtyRect) |
|
6329 { |
|
6330 |
|
6331 uint32_t startRowIndex, endRowIndex, startColIndex, endColIndex; |
|
6332 startRowIndex = endRowIndex = startColIndex = endColIndex = 0; |
|
6333 bool done = false; |
|
6334 bool haveIntersect = false; |
|
6335 // find startRowIndex, endRowIndex |
|
6336 nscoord rowY = mInitialOffsetY; |
|
6337 for (uint32_t rgX = 0; rgX < mRowGroups.Length() && !done; rgX++) { |
|
6338 nsTableRowGroupFrame* rgFrame = mRowGroups[rgX]; |
|
6339 for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame; |
|
6340 rowFrame = rowFrame->GetNextRow()) { |
|
6341 // conservatively estimate the half border widths outside the row |
|
6342 nscoord topBorderHalf = (mTable->GetPrevInFlow()) ? 0 : |
|
6343 nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1); |
|
6344 nscoord bottomBorderHalf = (mTable->GetNextInFlow()) ? 0 : |
|
6345 nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1); |
|
6346 // get the row rect relative to the table rather than the row group |
|
6347 nsSize rowSize = rowFrame->GetSize(); |
|
6348 if (haveIntersect) { |
|
6349 if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) { |
|
6350 nsTableRowFrame* fifRow = |
|
6351 static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow()); |
|
6352 endRowIndex = fifRow->GetRowIndex(); |
|
6353 } |
|
6354 else done = true; |
|
6355 } |
|
6356 else { |
|
6357 if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) { |
|
6358 mStartRg = rgFrame; |
|
6359 mStartRow = rowFrame; |
|
6360 nsTableRowFrame* fifRow = |
|
6361 static_cast<nsTableRowFrame*>(rowFrame->FirstInFlow()); |
|
6362 startRowIndex = endRowIndex = fifRow->GetRowIndex(); |
|
6363 haveIntersect = true; |
|
6364 } |
|
6365 else { |
|
6366 mInitialOffsetY += rowSize.height; |
|
6367 } |
|
6368 } |
|
6369 rowY += rowSize.height; |
|
6370 } |
|
6371 } |
|
6372 mNextOffsetY = mInitialOffsetY; |
|
6373 |
|
6374 // XXX comment refers to the obsolete NS_FRAME_OUTSIDE_CHILDREN flag |
|
6375 // XXX but I don't understand it, so not changing it for now |
|
6376 // outer table borders overflow the table, so the table might be |
|
6377 // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set |
|
6378 // on the table |
|
6379 if (!haveIntersect) |
|
6380 return false; |
|
6381 // find startColIndex, endColIndex, startColX |
|
6382 haveIntersect = false; |
|
6383 if (0 == mNumTableCols) |
|
6384 return false; |
|
6385 int32_t leftCol, rightCol; // columns are in the range [leftCol, rightCol) |
|
6386 |
|
6387 nsMargin childAreaOffset = mTable->GetChildAreaOffset(nullptr); |
|
6388 if (mTableIsLTR) { |
|
6389 mInitialOffsetX = childAreaOffset.left; // x position of first col in |
|
6390 // damage area |
|
6391 leftCol = 0; |
|
6392 rightCol = mNumTableCols; |
|
6393 } else { |
|
6394 // x position of first col in damage area |
|
6395 mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; |
|
6396 leftCol = mNumTableCols-1; |
|
6397 rightCol = -1; |
|
6398 } |
|
6399 nscoord x = 0; |
|
6400 int32_t colX; |
|
6401 for (colX = leftCol; colX != rightCol; colX += mColInc) { |
|
6402 nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(colX); |
|
6403 if (!colFrame) ABORT1(false); |
|
6404 // get the col rect relative to the table rather than the col group |
|
6405 nsSize size = colFrame->GetSize(); |
|
6406 if (haveIntersect) { |
|
6407 // conservatively estimate the left half border width outside the col |
|
6408 nscoord leftBorderHalf = |
|
6409 nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1); |
|
6410 if (aDirtyRect.XMost() >= (x - leftBorderHalf)) { |
|
6411 endColIndex = colX; |
|
6412 } |
|
6413 else break; |
|
6414 } |
|
6415 else { |
|
6416 // conservatively estimate the right half border width outside the col |
|
6417 nscoord rightBorderHalf = |
|
6418 nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1); |
|
6419 if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) { |
|
6420 startColIndex = endColIndex = colX; |
|
6421 haveIntersect = true; |
|
6422 } |
|
6423 else { |
|
6424 mInitialOffsetX += mColInc * size.width; |
|
6425 } |
|
6426 } |
|
6427 x += size.width; |
|
6428 } |
|
6429 if (!mTableIsLTR) { |
|
6430 uint32_t temp; |
|
6431 mInitialOffsetX = mTable->GetRect().width - childAreaOffset.right; |
|
6432 temp = startColIndex; startColIndex = endColIndex; endColIndex = temp; |
|
6433 for (uint32_t column = 0; column < startColIndex; column++) { |
|
6434 nsTableColFrame* colFrame = mTableFirstInFlow->GetColFrame(column); |
|
6435 if (!colFrame) ABORT1(false); |
|
6436 nsSize size = colFrame->GetSize(); |
|
6437 mInitialOffsetX += mColInc * size.width; |
|
6438 } |
|
6439 } |
|
6440 if (!haveIntersect) |
|
6441 return false; |
|
6442 mDamageArea = nsIntRect(startColIndex, startRowIndex, |
|
6443 1 + DeprecatedAbs<int32_t>(endColIndex - startColIndex), |
|
6444 1 + endRowIndex - startRowIndex); |
|
6445 |
|
6446 Reset(); |
|
6447 mVerInfo = new BCVerticalSeg[mDamageArea.width + 1]; |
|
6448 if (!mVerInfo) |
|
6449 return false; |
|
6450 return true; |
|
6451 } |
|
6452 |
|
6453 void |
|
6454 BCPaintBorderIterator::Reset() |
|
6455 { |
|
6456 mAtEnd = true; // gets reset when First() is called |
|
6457 mRg = mStartRg; |
|
6458 mPrevRow = nullptr; |
|
6459 mRow = mStartRow; |
|
6460 mRowIndex = 0; |
|
6461 mColIndex = 0; |
|
6462 mRgIndex = -1; |
|
6463 mPrevCell = nullptr; |
|
6464 mCell = nullptr; |
|
6465 mPrevCellData = nullptr; |
|
6466 mCellData = nullptr; |
|
6467 mBCData = nullptr; |
|
6468 ResetVerInfo(); |
|
6469 } |
|
6470 |
|
6471 /** |
|
6472 * Set the iterator data to a new cellmap coordinate |
|
6473 * @param aRowIndex - the row index |
|
6474 * @param aColIndex - the col index |
|
6475 */ |
|
6476 void |
|
6477 BCPaintBorderIterator::SetNewData(int32_t aY, |
|
6478 int32_t aX) |
|
6479 { |
|
6480 if (!mTableCellMap || !mTableCellMap->mBCInfo) ABORT0(); |
|
6481 |
|
6482 mColIndex = aX; |
|
6483 mRowIndex = aY; |
|
6484 mPrevCellData = mCellData; |
|
6485 if (IsTableRightMost() && IsTableBottomMost()) { |
|
6486 mCell = nullptr; |
|
6487 mBCData = &mTableCellMap->mBCInfo->mLowerRightCorner; |
|
6488 } |
|
6489 else if (IsTableRightMost()) { |
|
6490 mCellData = nullptr; |
|
6491 mBCData = &mTableCellMap->mBCInfo->mRightBorders.ElementAt(aY); |
|
6492 } |
|
6493 else if (IsTableBottomMost()) { |
|
6494 mCellData = nullptr; |
|
6495 mBCData = &mTableCellMap->mBCInfo->mBottomBorders.ElementAt(aX); |
|
6496 } |
|
6497 else { |
|
6498 if (uint32_t(mRowIndex - mFifRgFirstRowIndex) < mCellMap->mRows.Length()) { |
|
6499 mBCData = nullptr; |
|
6500 mCellData = |
|
6501 (BCCellData*)mCellMap->mRows[mRowIndex - mFifRgFirstRowIndex].SafeElementAt(mColIndex); |
|
6502 if (mCellData) { |
|
6503 mBCData = &mCellData->mData; |
|
6504 if (!mCellData->IsOrig()) { |
|
6505 if (mCellData->IsRowSpan()) { |
|
6506 aY -= mCellData->GetRowSpanOffset(); |
|
6507 } |
|
6508 if (mCellData->IsColSpan()) { |
|
6509 aX -= mCellData->GetColSpanOffset(); |
|
6510 } |
|
6511 if ((aX >= 0) && (aY >= 0)) { |
|
6512 mCellData = (BCCellData*)mCellMap->mRows[aY - mFifRgFirstRowIndex][aX]; |
|
6513 } |
|
6514 } |
|
6515 if (mCellData->IsOrig()) { |
|
6516 mPrevCell = mCell; |
|
6517 mCell = mCellData->GetCellFrame(); |
|
6518 } |
|
6519 } |
|
6520 } |
|
6521 } |
|
6522 } |
|
6523 |
|
6524 /** |
|
6525 * Set the iterator to a new row |
|
6526 * @param aRow - the new row frame, if null the iterator will advance to the |
|
6527 * next row |
|
6528 */ |
|
6529 bool |
|
6530 BCPaintBorderIterator::SetNewRow(nsTableRowFrame* aRow) |
|
6531 { |
|
6532 mPrevRow = mRow; |
|
6533 mRow = (aRow) ? aRow : mRow->GetNextRow(); |
|
6534 if (mRow) { |
|
6535 mIsNewRow = true; |
|
6536 mRowIndex = mRow->GetRowIndex(); |
|
6537 mColIndex = mDamageArea.x; |
|
6538 mPrevHorSegHeight = 0; |
|
6539 if (mIsRepeatedHeader) { |
|
6540 mRepeatedHeaderRowIndex = mRowIndex; |
|
6541 } |
|
6542 } |
|
6543 else { |
|
6544 mAtEnd = true; |
|
6545 } |
|
6546 return !mAtEnd; |
|
6547 } |
|
6548 |
|
6549 /** |
|
6550 * Advance the iterator to the next row group |
|
6551 */ |
|
6552 bool |
|
6553 BCPaintBorderIterator::SetNewRowGroup() |
|
6554 { |
|
6555 |
|
6556 mRgIndex++; |
|
6557 |
|
6558 mIsRepeatedHeader = false; |
|
6559 mIsRepeatedFooter = false; |
|
6560 |
|
6561 NS_ASSERTION(mRgIndex >= 0, "mRgIndex out of bounds"); |
|
6562 if (uint32_t(mRgIndex) < mRowGroups.Length()) { |
|
6563 mPrevRg = mRg; |
|
6564 mRg = mRowGroups[mRgIndex]; |
|
6565 nsTableRowGroupFrame* fifRg = |
|
6566 static_cast<nsTableRowGroupFrame*>(mRg->FirstInFlow()); |
|
6567 mFifRgFirstRowIndex = fifRg->GetStartRowIndex(); |
|
6568 mRgFirstRowIndex = mRg->GetStartRowIndex(); |
|
6569 mRgLastRowIndex = mRgFirstRowIndex + mRg->GetRowCount() - 1; |
|
6570 |
|
6571 if (SetNewRow(mRg->GetFirstRow())) { |
|
6572 mCellMap = mTableCellMap->GetMapFor(fifRg, nullptr); |
|
6573 if (!mCellMap) ABORT1(false); |
|
6574 } |
|
6575 if (mRg && mTable->GetPrevInFlow() && !mRg->GetPrevInFlow()) { |
|
6576 // if mRowGroup doesn't have a prev in flow, then it may be a repeated |
|
6577 // header or footer |
|
6578 const nsStyleDisplay* display = mRg->StyleDisplay(); |
|
6579 if (mRowIndex == mDamageArea.y) { |
|
6580 mIsRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay); |
|
6581 } |
|
6582 else { |
|
6583 mIsRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay); |
|
6584 } |
|
6585 } |
|
6586 } |
|
6587 else { |
|
6588 mAtEnd = true; |
|
6589 } |
|
6590 return !mAtEnd; |
|
6591 } |
|
6592 |
|
6593 /** |
|
6594 * Move the iterator to the first position in the damageArea |
|
6595 */ |
|
6596 void |
|
6597 BCPaintBorderIterator::First() |
|
6598 { |
|
6599 if (!mTable || (mDamageArea.x >= mNumTableCols) || |
|
6600 (mDamageArea.y >= mNumTableRows)) ABORT0(); |
|
6601 |
|
6602 mAtEnd = false; |
|
6603 |
|
6604 uint32_t numRowGroups = mRowGroups.Length(); |
|
6605 for (uint32_t rgY = 0; rgY < numRowGroups; rgY++) { |
|
6606 nsTableRowGroupFrame* rowG = mRowGroups[rgY]; |
|
6607 int32_t start = rowG->GetStartRowIndex(); |
|
6608 int32_t end = start + rowG->GetRowCount() - 1; |
|
6609 if ((mDamageArea.y >= start) && (mDamageArea.y <= end)) { |
|
6610 mRgIndex = rgY - 1; // SetNewRowGroup increments rowGroupIndex |
|
6611 if (SetNewRowGroup()) { |
|
6612 while ((mRowIndex < mDamageArea.y) && !mAtEnd) { |
|
6613 SetNewRow(); |
|
6614 } |
|
6615 if (!mAtEnd) { |
|
6616 SetNewData(mDamageArea.y, mDamageArea.x); |
|
6617 } |
|
6618 } |
|
6619 return; |
|
6620 } |
|
6621 } |
|
6622 mAtEnd = true; |
|
6623 } |
|
6624 |
|
6625 /** |
|
6626 * Advance the iterator to the next position |
|
6627 */ |
|
6628 void |
|
6629 BCPaintBorderIterator::Next() |
|
6630 { |
|
6631 if (mAtEnd) ABORT0(); |
|
6632 mIsNewRow = false; |
|
6633 |
|
6634 mColIndex++; |
|
6635 if (mColIndex > mDamageArea.XMost()) { |
|
6636 mRowIndex++; |
|
6637 if (mRowIndex == mDamageArea.YMost()) { |
|
6638 mColIndex = mDamageArea.x; |
|
6639 } |
|
6640 else if (mRowIndex < mDamageArea.YMost()) { |
|
6641 if (mRowIndex <= mRgLastRowIndex) { |
|
6642 SetNewRow(); |
|
6643 } |
|
6644 else { |
|
6645 SetNewRowGroup(); |
|
6646 } |
|
6647 } |
|
6648 else { |
|
6649 mAtEnd = true; |
|
6650 } |
|
6651 } |
|
6652 if (!mAtEnd) { |
|
6653 SetNewData(mRowIndex, mColIndex); |
|
6654 } |
|
6655 } |
|
6656 |
|
6657 // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine |
|
6658 // them |
|
6659 /** Compute the vertical offset of a vertical border segment |
|
6660 * @param aCornerOwnerSide - which side owns the corner |
|
6661 * @param aCornerSubWidth - how wide is the nonwinning side of the corner |
|
6662 * @param aHorWidth - how wide is the horizontal edge of the corner |
|
6663 * @param aIsStartOfSeg - does this corner start a new segment |
|
6664 * @param aIsBevel - is this corner beveled |
|
6665 * @return - offset in twips |
|
6666 */ |
|
6667 static nscoord |
|
6668 CalcVerCornerOffset(mozilla::css::Side aCornerOwnerSide, |
|
6669 BCPixelSize aCornerSubWidth, |
|
6670 BCPixelSize aHorWidth, |
|
6671 bool aIsStartOfSeg, |
|
6672 bool aIsBevel) |
|
6673 { |
|
6674 nscoord offset = 0; |
|
6675 // XXX These should be replaced with appropriate side-specific macros (which?) |
|
6676 BCPixelSize smallHalf, largeHalf; |
|
6677 if ((NS_SIDE_TOP == aCornerOwnerSide) || |
|
6678 (NS_SIDE_BOTTOM == aCornerOwnerSide)) { |
|
6679 DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); |
|
6680 if (aIsBevel) { |
|
6681 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
|
6682 } |
|
6683 else { |
|
6684 offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf; |
|
6685 } |
|
6686 } |
|
6687 else { |
|
6688 DivideBCBorderSize(aHorWidth, smallHalf, largeHalf); |
|
6689 if (aIsBevel) { |
|
6690 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
|
6691 } |
|
6692 else { |
|
6693 offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; |
|
6694 } |
|
6695 } |
|
6696 return nsPresContext::CSSPixelsToAppUnits(offset); |
|
6697 } |
|
6698 |
|
6699 /** Compute the horizontal offset of a horizontal border segment |
|
6700 * @param aCornerOwnerSide - which side owns the corner |
|
6701 * @param aCornerSubWidth - how wide is the nonwinning side of the corner |
|
6702 * @param aVerWidth - how wide is the vertical edge of the corner |
|
6703 * @param aIsStartOfSeg - does this corner start a new segment |
|
6704 * @param aIsBevel - is this corner beveled |
|
6705 * @param aTableIsLTR - direction, the computation depends on ltr or rtl |
|
6706 * @return - offset in twips |
|
6707 */ |
|
6708 static nscoord |
|
6709 CalcHorCornerOffset(mozilla::css::Side aCornerOwnerSide, |
|
6710 BCPixelSize aCornerSubWidth, |
|
6711 BCPixelSize aVerWidth, |
|
6712 bool aIsStartOfSeg, |
|
6713 bool aIsBevel, |
|
6714 bool aTableIsLTR) |
|
6715 { |
|
6716 nscoord offset = 0; |
|
6717 // XXX These should be replaced with appropriate side-specific macros (which?) |
|
6718 BCPixelSize smallHalf, largeHalf; |
|
6719 if ((NS_SIDE_LEFT == aCornerOwnerSide) || |
|
6720 (NS_SIDE_RIGHT == aCornerOwnerSide)) { |
|
6721 if (aTableIsLTR) { |
|
6722 DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf); |
|
6723 } |
|
6724 else { |
|
6725 DivideBCBorderSize(aCornerSubWidth, largeHalf, smallHalf); |
|
6726 } |
|
6727 if (aIsBevel) { |
|
6728 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
|
6729 } |
|
6730 else { |
|
6731 offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf; |
|
6732 } |
|
6733 } |
|
6734 else { |
|
6735 if (aTableIsLTR) { |
|
6736 DivideBCBorderSize(aVerWidth, smallHalf, largeHalf); |
|
6737 } |
|
6738 else { |
|
6739 DivideBCBorderSize(aVerWidth, largeHalf, smallHalf); |
|
6740 } |
|
6741 if (aIsBevel) { |
|
6742 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf; |
|
6743 } |
|
6744 else { |
|
6745 offset = (aIsStartOfSeg) ? smallHalf : -largeHalf; |
|
6746 } |
|
6747 } |
|
6748 return nsPresContext::CSSPixelsToAppUnits(offset); |
|
6749 } |
|
6750 |
|
6751 BCVerticalSeg::BCVerticalSeg() |
|
6752 { |
|
6753 mCol = nullptr; |
|
6754 mFirstCell = mLastCell = mAjaCell = nullptr; |
|
6755 mOffsetX = mOffsetY = mLength = mWidth = mTopBevelOffset = 0; |
|
6756 mTopBevelSide = NS_SIDE_TOP; |
|
6757 mOwner = eCellOwner; |
|
6758 } |
|
6759 |
|
6760 /** |
|
6761 * Start a new vertical segment |
|
6762 * @param aIter - iterator containing the structural information |
|
6763 * @param aBorderOwner - determines the border style |
|
6764 * @param aVerSegWidth - the width of segment in pixel |
|
6765 * @param aHorSegHeight - the width of the horizontal segment joining the corner |
|
6766 * at the start |
|
6767 */ |
|
6768 void |
|
6769 BCVerticalSeg::Start(BCPaintBorderIterator& aIter, |
|
6770 BCBorderOwner aBorderOwner, |
|
6771 BCPixelSize aVerSegWidth, |
|
6772 BCPixelSize aHorSegHeight) |
|
6773 { |
|
6774 mozilla::css::Side ownerSide = NS_SIDE_TOP; |
|
6775 bool bevel = false; |
|
6776 |
|
6777 |
|
6778 nscoord cornerSubWidth = (aIter.mBCData) ? |
|
6779 aIter.mBCData->GetCorner(ownerSide, bevel) : 0; |
|
6780 |
|
6781 bool topBevel = (aVerSegWidth > 0) ? bevel : false; |
|
6782 BCPixelSize maxHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight); |
|
6783 nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, |
|
6784 maxHorSegHeight, true, |
|
6785 topBevel); |
|
6786 |
|
6787 mTopBevelOffset = topBevel ? |
|
6788 nsPresContext::CSSPixelsToAppUnits(maxHorSegHeight): 0; |
|
6789 // XXX this assumes that only corners where 2 segments join can be beveled |
|
6790 mTopBevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT; |
|
6791 mOffsetY += offset; |
|
6792 mLength = -offset; |
|
6793 mWidth = aVerSegWidth; |
|
6794 mOwner = aBorderOwner; |
|
6795 mFirstCell = aIter.mCell; |
|
6796 mFirstRowGroup = aIter.mRg; |
|
6797 mFirstRow = aIter.mRow; |
|
6798 if (aIter.GetRelativeColIndex() > 0) { |
|
6799 mAjaCell = aIter.mVerInfo[aIter.GetRelativeColIndex() - 1].mLastCell; |
|
6800 } |
|
6801 } |
|
6802 |
|
6803 /** |
|
6804 * Initialize the vertical segments with information that will persist for any |
|
6805 * vertical segment in this column |
|
6806 * @param aIter - iterator containing the structural information |
|
6807 */ |
|
6808 void |
|
6809 BCVerticalSeg::Initialize(BCPaintBorderIterator& aIter) |
|
6810 { |
|
6811 int32_t relColIndex = aIter.GetRelativeColIndex(); |
|
6812 mCol = aIter.IsTableRightMost() ? aIter.mVerInfo[relColIndex - 1].mCol : |
|
6813 aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex); |
|
6814 if (!mCol) ABORT0(); |
|
6815 if (0 == relColIndex) { |
|
6816 mOffsetX = aIter.mInitialOffsetX; |
|
6817 } |
|
6818 // set colX for the next column |
|
6819 if (!aIter.IsDamageAreaRightMost()) { |
|
6820 aIter.mVerInfo[relColIndex + 1].mOffsetX = mOffsetX + |
|
6821 aIter.mColInc * mCol->GetSize().width; |
|
6822 } |
|
6823 mOffsetY = aIter.mInitialOffsetY; |
|
6824 mLastCell = aIter.mCell; |
|
6825 } |
|
6826 |
|
6827 /** |
|
6828 * Compute the offsets for the bottom corner of a vertical segment |
|
6829 * @param aIter - iterator containing the structural information |
|
6830 * @param aHorSegHeight - the width of the horizontal segment joining the corner |
|
6831 * at the start |
|
6832 */ |
|
6833 void |
|
6834 BCVerticalSeg::GetBottomCorner(BCPaintBorderIterator& aIter, |
|
6835 BCPixelSize aHorSegHeight) |
|
6836 { |
|
6837 mozilla::css::Side ownerSide = NS_SIDE_TOP; |
|
6838 nscoord cornerSubWidth = 0; |
|
6839 bool bevel = false; |
|
6840 if (aIter.mBCData) { |
|
6841 cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); |
|
6842 } |
|
6843 mIsBottomBevel = (mWidth > 0) ? bevel : false; |
|
6844 mBottomHorSegHeight = std::max(aIter.mPrevHorSegHeight, aHorSegHeight); |
|
6845 mBottomOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, |
|
6846 mBottomHorSegHeight, |
|
6847 false, mIsBottomBevel); |
|
6848 mLength += mBottomOffset; |
|
6849 } |
|
6850 |
|
6851 /** |
|
6852 * Paint the vertical segment |
|
6853 * @param aIter - iterator containing the structural information |
|
6854 * @param aRenderingContext - the rendering context |
|
6855 * @param aHorSegHeight - the width of the horizontal segment joining the corner |
|
6856 * at the start |
|
6857 */ |
|
6858 void |
|
6859 BCVerticalSeg::Paint(BCPaintBorderIterator& aIter, |
|
6860 nsRenderingContext& aRenderingContext, |
|
6861 BCPixelSize aHorSegHeight) |
|
6862 { |
|
6863 // get the border style, color and paint the segment |
|
6864 mozilla::css::Side side = (aIter.IsDamageAreaRightMost()) ? NS_SIDE_RIGHT : |
|
6865 NS_SIDE_LEFT; |
|
6866 int32_t relColIndex = aIter.GetRelativeColIndex(); |
|
6867 nsTableColFrame* col = mCol; if (!col) ABORT0(); |
|
6868 nsTableCellFrame* cell = mFirstCell; // ??? |
|
6869 nsIFrame* owner = nullptr; |
|
6870 uint8_t style = NS_STYLE_BORDER_STYLE_SOLID; |
|
6871 nscolor color = 0xFFFFFFFF; |
|
6872 |
|
6873 switch (mOwner) { |
|
6874 case eTableOwner: |
|
6875 owner = aIter.mTable; |
|
6876 break; |
|
6877 case eAjaColGroupOwner: |
|
6878 side = NS_SIDE_RIGHT; |
|
6879 if (!aIter.IsTableRightMost() && (relColIndex > 0)) { |
|
6880 col = aIter.mVerInfo[relColIndex - 1].mCol; |
|
6881 } // and fall through |
|
6882 case eColGroupOwner: |
|
6883 if (col) { |
|
6884 owner = col->GetParent(); |
|
6885 } |
|
6886 break; |
|
6887 case eAjaColOwner: |
|
6888 side = NS_SIDE_RIGHT; |
|
6889 if (!aIter.IsTableRightMost() && (relColIndex > 0)) { |
|
6890 col = aIter.mVerInfo[relColIndex - 1].mCol; |
|
6891 } // and fall through |
|
6892 case eColOwner: |
|
6893 owner = col; |
|
6894 break; |
|
6895 case eAjaRowGroupOwner: |
|
6896 NS_ERROR("a neighboring rowgroup can never own a vertical border"); |
|
6897 // and fall through |
|
6898 case eRowGroupOwner: |
|
6899 NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), |
|
6900 "row group can own border only at table edge"); |
|
6901 owner = mFirstRowGroup; |
|
6902 break; |
|
6903 case eAjaRowOwner: |
|
6904 NS_ASSERTION(false, "program error"); // and fall through |
|
6905 case eRowOwner: |
|
6906 NS_ASSERTION(aIter.IsTableLeftMost() || aIter.IsTableRightMost(), |
|
6907 "row can own border only at table edge"); |
|
6908 owner = mFirstRow; |
|
6909 break; |
|
6910 case eAjaCellOwner: |
|
6911 side = NS_SIDE_RIGHT; |
|
6912 cell = mAjaCell; // and fall through |
|
6913 case eCellOwner: |
|
6914 owner = cell; |
|
6915 break; |
|
6916 } |
|
6917 if (owner) { |
|
6918 ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); |
|
6919 } |
|
6920 BCPixelSize smallHalf, largeHalf; |
|
6921 DivideBCBorderSize(mWidth, smallHalf, largeHalf); |
|
6922 nsRect segRect(mOffsetX - nsPresContext::CSSPixelsToAppUnits(largeHalf), |
|
6923 mOffsetY, |
|
6924 nsPresContext::CSSPixelsToAppUnits(mWidth), mLength); |
|
6925 nscoord bottomBevelOffset = (mIsBottomBevel) ? |
|
6926 nsPresContext::CSSPixelsToAppUnits(mBottomHorSegHeight) : 0; |
|
6927 mozilla::css::Side bottomBevelSide = ((aHorSegHeight > 0) ^ !aIter.mTableIsLTR) ? |
|
6928 NS_SIDE_RIGHT : NS_SIDE_LEFT; |
|
6929 mozilla::css::Side topBevelSide = ((mTopBevelSide == NS_SIDE_RIGHT) ^ !aIter.mTableIsLTR)? |
|
6930 NS_SIDE_RIGHT : NS_SIDE_LEFT; |
|
6931 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, |
|
6932 aIter.mTableBgColor, segRect, |
|
6933 nsPresContext::AppUnitsPerCSSPixel(), |
|
6934 topBevelSide, mTopBevelOffset, |
|
6935 bottomBevelSide, bottomBevelOffset); |
|
6936 } |
|
6937 |
|
6938 /** |
|
6939 * Advance the start point of a segment |
|
6940 */ |
|
6941 void |
|
6942 BCVerticalSeg::AdvanceOffsetY() |
|
6943 { |
|
6944 mOffsetY += mLength - mBottomOffset; |
|
6945 } |
|
6946 |
|
6947 /** |
|
6948 * Accumulate the current segment |
|
6949 */ |
|
6950 void |
|
6951 BCVerticalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) |
|
6952 { |
|
6953 mLastCell = aIter.mCell; |
|
6954 mLength += aIter.mRow->GetRect().height; |
|
6955 } |
|
6956 |
|
6957 BCHorizontalSeg::BCHorizontalSeg() |
|
6958 { |
|
6959 mOffsetX = mOffsetY = mLength = mWidth = mLeftBevelOffset = 0; |
|
6960 mLeftBevelSide = NS_SIDE_TOP; |
|
6961 mFirstCell = mAjaCell = nullptr; |
|
6962 } |
|
6963 |
|
6964 /** Initialize a horizontal border segment for painting |
|
6965 * @param aIter - iterator storing the current and adjacent frames |
|
6966 * @param aBorderOwner - which frame owns the border |
|
6967 * @param aBottomVerSegWidth - vertical segment width coming from up |
|
6968 * @param aHorSegHeight - the height of the segment |
|
6969 + */ |
|
6970 void |
|
6971 BCHorizontalSeg::Start(BCPaintBorderIterator& aIter, |
|
6972 BCBorderOwner aBorderOwner, |
|
6973 BCPixelSize aBottomVerSegWidth, |
|
6974 BCPixelSize aHorSegHeight) |
|
6975 { |
|
6976 mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP; |
|
6977 bool bevel = false; |
|
6978 |
|
6979 mOwner = aBorderOwner; |
|
6980 nscoord cornerSubWidth = (aIter.mBCData) ? |
|
6981 aIter.mBCData->GetCorner(cornerOwnerSide, |
|
6982 bevel) : 0; |
|
6983 |
|
6984 bool leftBevel = (aHorSegHeight > 0) ? bevel : false; |
|
6985 int32_t relColIndex = aIter.GetRelativeColIndex(); |
|
6986 nscoord maxVerSegWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, |
|
6987 aBottomVerSegWidth); |
|
6988 nscoord offset = CalcHorCornerOffset(cornerOwnerSide, cornerSubWidth, |
|
6989 maxVerSegWidth, true, leftBevel, |
|
6990 aIter.mTableIsLTR); |
|
6991 mLeftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0; |
|
6992 // XXX this assumes that only corners where 2 segments join can be beveled |
|
6993 mLeftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; |
|
6994 if (aIter.mTableIsLTR) { |
|
6995 mOffsetX += offset; |
|
6996 } |
|
6997 else { |
|
6998 mOffsetX -= offset; |
|
6999 } |
|
7000 mLength = -offset; |
|
7001 mWidth = aHorSegHeight; |
|
7002 mFirstCell = aIter.mCell; |
|
7003 mAjaCell = (aIter.IsDamageAreaTopMost()) ? nullptr : |
|
7004 aIter.mVerInfo[relColIndex].mLastCell; |
|
7005 } |
|
7006 |
|
7007 /** |
|
7008 * Compute the offsets for the right corner of a horizontal segment |
|
7009 * @param aIter - iterator containing the structural information |
|
7010 * @param aLeftSegWidth - the width of the vertical segment joining the corner |
|
7011 * at the start |
|
7012 */ |
|
7013 void |
|
7014 BCHorizontalSeg::GetRightCorner(BCPaintBorderIterator& aIter, |
|
7015 BCPixelSize aLeftSegWidth) |
|
7016 { |
|
7017 mozilla::css::Side ownerSide = NS_SIDE_TOP; |
|
7018 nscoord cornerSubWidth = 0; |
|
7019 bool bevel = false; |
|
7020 if (aIter.mBCData) { |
|
7021 cornerSubWidth = aIter.mBCData->GetCorner(ownerSide, bevel); |
|
7022 } |
|
7023 |
|
7024 mIsRightBevel = (mWidth > 0) ? bevel : 0; |
|
7025 int32_t relColIndex = aIter.GetRelativeColIndex(); |
|
7026 nscoord verWidth = std::max(aIter.mVerInfo[relColIndex].mWidth, aLeftSegWidth); |
|
7027 mEndOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, |
|
7028 false, mIsRightBevel, aIter.mTableIsLTR); |
|
7029 mLength += mEndOffset; |
|
7030 mRightBevelOffset = (mIsRightBevel) ? |
|
7031 nsPresContext::CSSPixelsToAppUnits(verWidth) : 0; |
|
7032 mRightBevelSide = (aLeftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP; |
|
7033 } |
|
7034 |
|
7035 /** |
|
7036 * Paint the horizontal segment |
|
7037 * @param aIter - iterator containing the structural information |
|
7038 * @param aRenderingContext - the rendering context |
|
7039 */ |
|
7040 void |
|
7041 BCHorizontalSeg::Paint(BCPaintBorderIterator& aIter, |
|
7042 nsRenderingContext& aRenderingContext) |
|
7043 { |
|
7044 // get the border style, color and paint the segment |
|
7045 mozilla::css::Side side = (aIter.IsDamageAreaBottomMost()) ? NS_SIDE_BOTTOM : |
|
7046 NS_SIDE_TOP; |
|
7047 nsIFrame* rg = aIter.mRg; if (!rg) ABORT0(); |
|
7048 nsIFrame* row = aIter.mRow; if (!row) ABORT0(); |
|
7049 nsIFrame* cell = mFirstCell; |
|
7050 nsIFrame* col; |
|
7051 nsIFrame* owner = nullptr; |
|
7052 |
|
7053 uint8_t style = NS_STYLE_BORDER_STYLE_SOLID; |
|
7054 nscolor color = 0xFFFFFFFF; |
|
7055 |
|
7056 |
|
7057 switch (mOwner) { |
|
7058 case eTableOwner: |
|
7059 owner = aIter.mTable; |
|
7060 break; |
|
7061 case eAjaColGroupOwner: |
|
7062 NS_ERROR("neighboring colgroups can never own a horizontal border"); |
|
7063 // and fall through |
|
7064 case eColGroupOwner: |
|
7065 NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), |
|
7066 "col group can own border only at the table edge"); |
|
7067 col = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); |
|
7068 if (!col) ABORT0(); |
|
7069 owner = col->GetParent(); |
|
7070 break; |
|
7071 case eAjaColOwner: |
|
7072 NS_ERROR("neighboring column can never own a horizontal border"); |
|
7073 // and fall through |
|
7074 case eColOwner: |
|
7075 NS_ASSERTION(aIter.IsTableTopMost() || aIter.IsTableBottomMost(), |
|
7076 "col can own border only at the table edge"); |
|
7077 owner = aIter.mTableFirstInFlow->GetColFrame(aIter.mColIndex - 1); |
|
7078 break; |
|
7079 case eAjaRowGroupOwner: |
|
7080 side = NS_SIDE_BOTTOM; |
|
7081 rg = (aIter.IsTableBottomMost()) ? aIter.mRg : aIter.mPrevRg; |
|
7082 // and fall through |
|
7083 case eRowGroupOwner: |
|
7084 owner = rg; |
|
7085 break; |
|
7086 case eAjaRowOwner: |
|
7087 side = NS_SIDE_BOTTOM; |
|
7088 row = (aIter.IsTableBottomMost()) ? aIter.mRow : aIter.mPrevRow; |
|
7089 // and fall through |
|
7090 case eRowOwner: |
|
7091 owner = row; |
|
7092 break; |
|
7093 case eAjaCellOwner: |
|
7094 side = NS_SIDE_BOTTOM; |
|
7095 // if this is null due to the damage area origin-y > 0, then the border |
|
7096 // won't show up anyway |
|
7097 cell = mAjaCell; |
|
7098 // and fall through |
|
7099 case eCellOwner: |
|
7100 owner = cell; |
|
7101 break; |
|
7102 } |
|
7103 if (owner) { |
|
7104 ::GetPaintStyleInfo(owner, side, style, color, aIter.mTableIsLTR); |
|
7105 } |
|
7106 BCPixelSize smallHalf, largeHalf; |
|
7107 DivideBCBorderSize(mWidth, smallHalf, largeHalf); |
|
7108 nsRect segRect(mOffsetX, |
|
7109 mOffsetY - nsPresContext::CSSPixelsToAppUnits(largeHalf), |
|
7110 mLength, |
|
7111 nsPresContext::CSSPixelsToAppUnits(mWidth)); |
|
7112 if (aIter.mTableIsLTR) { |
|
7113 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, |
|
7114 aIter.mTableBgColor, segRect, |
|
7115 nsPresContext::AppUnitsPerCSSPixel(), |
|
7116 mLeftBevelSide, |
|
7117 nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset), |
|
7118 mRightBevelSide, mRightBevelOffset); |
|
7119 } |
|
7120 else { |
|
7121 segRect.x -= segRect.width; |
|
7122 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, |
|
7123 aIter.mTableBgColor, segRect, |
|
7124 nsPresContext::AppUnitsPerCSSPixel(), |
|
7125 mRightBevelSide, mRightBevelOffset, |
|
7126 mLeftBevelSide, |
|
7127 nsPresContext::CSSPixelsToAppUnits(mLeftBevelOffset)); |
|
7128 } |
|
7129 } |
|
7130 |
|
7131 /** |
|
7132 * Advance the start point of a segment |
|
7133 */ |
|
7134 void |
|
7135 BCHorizontalSeg::AdvanceOffsetX(int32_t aIncrement) |
|
7136 { |
|
7137 mOffsetX += aIncrement * (mLength - mEndOffset); |
|
7138 } |
|
7139 |
|
7140 /** |
|
7141 * Accumulate the current segment |
|
7142 */ |
|
7143 void |
|
7144 BCHorizontalSeg::IncludeCurrentBorder(BCPaintBorderIterator& aIter) |
|
7145 { |
|
7146 mLength += aIter.mVerInfo[aIter.GetRelativeColIndex()].mColWidth; |
|
7147 } |
|
7148 |
|
7149 /** |
|
7150 * store the column width information while painting horizontal segment |
|
7151 */ |
|
7152 void |
|
7153 BCPaintBorderIterator::StoreColumnWidth(int32_t aIndex) |
|
7154 { |
|
7155 if (IsTableRightMost()) { |
|
7156 mVerInfo[aIndex].mColWidth = mVerInfo[aIndex - 1].mColWidth; |
|
7157 } |
|
7158 else { |
|
7159 nsTableColFrame* col = mTableFirstInFlow->GetColFrame(mColIndex); |
|
7160 if (!col) ABORT0(); |
|
7161 mVerInfo[aIndex].mColWidth = col->GetSize().width; |
|
7162 } |
|
7163 } |
|
7164 /** |
|
7165 * Determine if a vertical segment owns the corder |
|
7166 */ |
|
7167 bool |
|
7168 BCPaintBorderIterator::VerticalSegmentOwnsCorner() |
|
7169 { |
|
7170 mozilla::css::Side cornerOwnerSide = NS_SIDE_TOP; |
|
7171 bool bevel = false; |
|
7172 if (mBCData) { |
|
7173 mBCData->GetCorner(cornerOwnerSide, bevel); |
|
7174 } |
|
7175 // unitialized ownerside, bevel |
|
7176 return (NS_SIDE_TOP == cornerOwnerSide) || |
|
7177 (NS_SIDE_BOTTOM == cornerOwnerSide); |
|
7178 } |
|
7179 |
|
7180 /** |
|
7181 * Paint if necessary a horizontal segment, otherwise accumulate it |
|
7182 * @param aRenderingContext - the rendering context |
|
7183 */ |
|
7184 void |
|
7185 BCPaintBorderIterator::AccumulateOrPaintHorizontalSegment(nsRenderingContext& aRenderingContext) |
|
7186 { |
|
7187 |
|
7188 int32_t relColIndex = GetRelativeColIndex(); |
|
7189 // store the current col width if it hasn't been already |
|
7190 if (mVerInfo[relColIndex].mColWidth < 0) { |
|
7191 StoreColumnWidth(relColIndex); |
|
7192 } |
|
7193 |
|
7194 BCBorderOwner borderOwner = eCellOwner; |
|
7195 BCBorderOwner ignoreBorderOwner; |
|
7196 bool isSegStart = true; |
|
7197 bool ignoreSegStart; |
|
7198 |
|
7199 nscoord leftSegWidth = |
|
7200 mBCData ? mBCData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0; |
|
7201 nscoord topSegHeight = |
|
7202 mBCData ? mBCData->GetTopEdge(borderOwner, isSegStart) : 0; |
|
7203 |
|
7204 if (mIsNewRow || (IsDamageAreaLeftMost() && IsDamageAreaBottomMost())) { |
|
7205 // reset for every new row and on the bottom of the last row |
|
7206 mHorSeg.mOffsetY = mNextOffsetY; |
|
7207 mNextOffsetY = mNextOffsetY + mRow->GetSize().height; |
|
7208 mHorSeg.mOffsetX = mInitialOffsetX; |
|
7209 mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); |
|
7210 } |
|
7211 |
|
7212 if (!IsDamageAreaLeftMost() && (isSegStart || IsDamageAreaRightMost() || |
|
7213 VerticalSegmentOwnsCorner())) { |
|
7214 // paint the previous seg or the current one if IsDamageAreaRightMost() |
|
7215 if (mHorSeg.mLength > 0) { |
|
7216 mHorSeg.GetRightCorner(*this, leftSegWidth); |
|
7217 if (mHorSeg.mWidth > 0) { |
|
7218 mHorSeg.Paint(*this, aRenderingContext); |
|
7219 } |
|
7220 mHorSeg.AdvanceOffsetX(mColInc); |
|
7221 } |
|
7222 mHorSeg.Start(*this, borderOwner, leftSegWidth, topSegHeight); |
|
7223 } |
|
7224 mHorSeg.IncludeCurrentBorder(*this); |
|
7225 mVerInfo[relColIndex].mWidth = leftSegWidth; |
|
7226 mVerInfo[relColIndex].mLastCell = mCell; |
|
7227 } |
|
7228 /** |
|
7229 * Paint if necessary a vertical segment, otherwise it |
|
7230 * @param aRenderingContext - the rendering context |
|
7231 */ |
|
7232 void |
|
7233 BCPaintBorderIterator::AccumulateOrPaintVerticalSegment(nsRenderingContext& aRenderingContext) |
|
7234 { |
|
7235 BCBorderOwner borderOwner = eCellOwner; |
|
7236 BCBorderOwner ignoreBorderOwner; |
|
7237 bool isSegStart = true; |
|
7238 bool ignoreSegStart; |
|
7239 |
|
7240 nscoord verSegWidth = |
|
7241 mBCData ? mBCData->GetLeftEdge(borderOwner, isSegStart) : 0; |
|
7242 nscoord horSegHeight = |
|
7243 mBCData ? mBCData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0; |
|
7244 |
|
7245 int32_t relColIndex = GetRelativeColIndex(); |
|
7246 BCVerticalSeg& verSeg = mVerInfo[relColIndex]; |
|
7247 if (!verSeg.mCol) { // on the first damaged row and the first segment in the |
|
7248 // col |
|
7249 verSeg.Initialize(*this); |
|
7250 verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); |
|
7251 } |
|
7252 |
|
7253 if (!IsDamageAreaTopMost() && (isSegStart || IsDamageAreaBottomMost() || |
|
7254 IsAfterRepeatedHeader() || |
|
7255 StartRepeatedFooter())) { |
|
7256 // paint the previous seg or the current one if IsDamageAreaBottomMost() |
|
7257 if (verSeg.mLength > 0) { |
|
7258 verSeg.GetBottomCorner(*this, horSegHeight); |
|
7259 if (verSeg.mWidth > 0) { |
|
7260 verSeg.Paint(*this, aRenderingContext, horSegHeight); |
|
7261 } |
|
7262 verSeg.AdvanceOffsetY(); |
|
7263 } |
|
7264 verSeg.Start(*this, borderOwner, verSegWidth, horSegHeight); |
|
7265 } |
|
7266 verSeg.IncludeCurrentBorder(*this); |
|
7267 mPrevHorSegHeight = horSegHeight; |
|
7268 } |
|
7269 |
|
7270 /** |
|
7271 * Reset the vertical information cache |
|
7272 */ |
|
7273 void |
|
7274 BCPaintBorderIterator::ResetVerInfo() |
|
7275 { |
|
7276 if (mVerInfo) { |
|
7277 memset(mVerInfo, 0, mDamageArea.width * sizeof(BCVerticalSeg)); |
|
7278 // XXX reinitialize properly |
|
7279 for (int32_t xIndex = 0; xIndex < mDamageArea.width; xIndex++) { |
|
7280 mVerInfo[xIndex].mColWidth = -1; |
|
7281 } |
|
7282 } |
|
7283 } |
|
7284 |
|
7285 /** |
|
7286 * Method to paint BCBorders, this does not use currently display lists although |
|
7287 * it will do this in future |
|
7288 * @param aRenderingContext - the rendering context |
|
7289 * @param aDirtyRect - inside this rectangle the BC Borders will redrawn |
|
7290 */ |
|
7291 void |
|
7292 nsTableFrame::PaintBCBorders(nsRenderingContext& aRenderingContext, |
|
7293 const nsRect& aDirtyRect) |
|
7294 { |
|
7295 // We first transfer the aDirtyRect into cellmap coordinates to compute which |
|
7296 // cell borders need to be painted |
|
7297 BCPaintBorderIterator iter(this); |
|
7298 if (!iter.SetDamageArea(aDirtyRect)) |
|
7299 return; |
|
7300 |
|
7301 // First, paint all of the vertical borders from top to bottom and left to |
|
7302 // right as they become complete. They are painted first, since they are less |
|
7303 // efficient to paint than horizontal segments. They were stored with as few |
|
7304 // segments as possible (since horizontal borders are painted last and |
|
7305 // possibly over them). For every cell in a row that fails in the damage are |
|
7306 // we look up if the current border would start a new segment, if so we paint |
|
7307 // the previously stored vertical segment and start a new segment. After |
|
7308 // this we the now active segment with the current border. These |
|
7309 // segments are stored in mVerInfo to be used on the next row |
|
7310 for (iter.First(); !iter.mAtEnd; iter.Next()) { |
|
7311 iter.AccumulateOrPaintVerticalSegment(aRenderingContext); |
|
7312 } |
|
7313 |
|
7314 // Next, paint all of the horizontal border segments from top to bottom reuse |
|
7315 // the mVerInfo array to keep track of col widths and vertical segments for |
|
7316 // corner calculations |
|
7317 iter.Reset(); |
|
7318 for (iter.First(); !iter.mAtEnd; iter.Next()) { |
|
7319 iter.AccumulateOrPaintHorizontalSegment(aRenderingContext); |
|
7320 } |
|
7321 } |
|
7322 |
|
7323 bool nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) |
|
7324 { |
|
7325 bool result = false; |
|
7326 nsTableCellMap* cellMap = GetCellMap(); |
|
7327 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated."); |
|
7328 if (cellMap) { |
|
7329 result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols); |
|
7330 } |
|
7331 return result; |
|
7332 } |
|
7333 |
|
7334 bool nsTableFrame::RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) |
|
7335 { |
|
7336 bool result = false; |
|
7337 nsTableCellMap* cellMap = GetCellMap(); |
|
7338 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated."); |
|
7339 if (cellMap) { |
|
7340 result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols); |
|
7341 } |
|
7342 return result; |
|
7343 } |
|
7344 |
|
7345 /* static */ |
|
7346 void |
|
7347 nsTableFrame::InvalidateTableFrame(nsIFrame* aFrame, |
|
7348 const nsRect& aOrigRect, |
|
7349 const nsRect& aOrigVisualOverflow, |
|
7350 bool aIsFirstReflow) |
|
7351 { |
|
7352 nsIFrame* parent = aFrame->GetParent(); |
|
7353 NS_ASSERTION(parent, "What happened here?"); |
|
7354 |
|
7355 if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) { |
|
7356 // Don't bother; we'll invalidate the parent's overflow rect when |
|
7357 // we finish reflowing it. |
|
7358 return; |
|
7359 } |
|
7360 |
|
7361 // The part that looks at both the rect and the overflow rect is a |
|
7362 // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent |
|
7363 // description of its hackishness. |
|
7364 // |
|
7365 // This doesn't really make sense now that we have DLBI. |
|
7366 // This code can probably be simplified a fair bit. |
|
7367 nsRect visualOverflow = aFrame->GetVisualOverflowRect(); |
|
7368 if (aIsFirstReflow || |
|
7369 aOrigRect.TopLeft() != aFrame->GetPosition() || |
|
7370 aOrigVisualOverflow.TopLeft() != visualOverflow.TopLeft()) { |
|
7371 // Invalidate the old and new overflow rects. Note that if the |
|
7372 // frame moved, we can't just use aOrigVisualOverflow, since it's in |
|
7373 // coordinates relative to the old position. So invalidate via |
|
7374 // aFrame's parent, and reposition that overflow rect to the right |
|
7375 // place. |
|
7376 // XXXbz this doesn't handle outlines, does it? |
|
7377 aFrame->InvalidateFrame(); |
|
7378 parent->InvalidateFrameWithRect(aOrigVisualOverflow + aOrigRect.TopLeft()); |
|
7379 } else if (aOrigRect.Size() != aFrame->GetSize() || |
|
7380 aOrigVisualOverflow.Size() != visualOverflow.Size()){ |
|
7381 aFrame->InvalidateFrameWithRect(aOrigVisualOverflow); |
|
7382 aFrame->InvalidateFrame(); |
|
7383 parent->InvalidateFrameWithRect(aOrigRect);; |
|
7384 parent->InvalidateFrame(); |
|
7385 } |
|
7386 } |