Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 : #include "nsCOMPtr.h"
6 : #include "nsTableRowGroupFrame.h"
7 : #include "nsTableRowFrame.h"
8 : #include "nsTableFrame.h"
9 : #include "nsTableCellFrame.h"
10 : #include "nsPresContext.h"
11 : #include "nsStyleContext.h"
12 : #include "nsStyleConsts.h"
13 : #include "nsIContent.h"
14 : #include "nsGkAtoms.h"
15 : #include "nsIPresShell.h"
16 : #include "nsCSSRendering.h"
17 : #include "nsHTMLParts.h"
18 : #include "nsCSSFrameConstructor.h"
19 : #include "nsDisplayList.h"
20 :
21 : #include "nsCellMap.h"//table cell navigation
22 : #include <algorithm>
23 :
24 : using namespace mozilla;
25 : using namespace mozilla::layout;
26 :
27 : namespace mozilla {
28 :
29 : struct TableRowGroupReflowInput {
30 : const ReflowInput& reflowInput; // Our reflow state
31 :
32 : nsTableFrame* tableFrame;
33 :
34 : // The available size (computed from the parent)
35 : mozilla::LogicalSize availSize;
36 :
37 : // Running block-offset
38 : nscoord bCoord;
39 :
40 0 : TableRowGroupReflowInput(const ReflowInput& aReflowInput,
41 : nsTableFrame* aTableFrame)
42 0 : : reflowInput(aReflowInput)
43 : , tableFrame(aTableFrame)
44 : , availSize(aReflowInput.GetWritingMode(),
45 : aReflowInput.AvailableISize(),
46 : aReflowInput.AvailableBSize())
47 0 : , bCoord(0)
48 : {
49 0 : }
50 :
51 0 : ~TableRowGroupReflowInput() {}
52 : };
53 :
54 : } // namespace mozilla
55 :
56 0 : nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext)
57 0 : : nsContainerFrame(aContext, kClassID)
58 : {
59 0 : SetRepeatable(false);
60 0 : }
61 :
62 0 : nsTableRowGroupFrame::~nsTableRowGroupFrame()
63 : {
64 0 : }
65 :
66 : void
67 0 : nsTableRowGroupFrame::DestroyFrom(nsIFrame* aDestructRoot)
68 : {
69 0 : if (HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
70 0 : nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
71 : }
72 :
73 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
74 0 : }
75 :
76 0 : NS_QUERYFRAME_HEAD(nsTableRowGroupFrame)
77 0 : NS_QUERYFRAME_ENTRY(nsTableRowGroupFrame)
78 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
79 :
80 : int32_t
81 0 : nsTableRowGroupFrame::GetRowCount()
82 : {
83 : #ifdef DEBUG
84 0 : for (nsFrameList::Enumerator e(mFrames); !e.AtEnd(); e.Next()) {
85 0 : NS_ASSERTION(e.get()->StyleDisplay()->mDisplay ==
86 : mozilla::StyleDisplay::TableRow,
87 : "Unexpected display");
88 0 : NS_ASSERTION(e.get()->IsTableRowFrame(), "Unexpected frame type");
89 : }
90 : #endif
91 :
92 0 : return mFrames.GetLength();
93 : }
94 :
95 0 : int32_t nsTableRowGroupFrame::GetStartRowIndex()
96 : {
97 0 : int32_t result = -1;
98 0 : if (mFrames.NotEmpty()) {
99 0 : NS_ASSERTION(mFrames.FirstChild()->IsTableRowFrame(),
100 : "Unexpected frame type");
101 0 : result = static_cast<nsTableRowFrame*>(mFrames.FirstChild())->GetRowIndex();
102 : }
103 : // if the row group doesn't have any children, get it the hard way
104 0 : if (-1 == result) {
105 0 : return GetTableFrame()->GetStartRowIndex(this);
106 : }
107 :
108 0 : return result;
109 : }
110 :
111 0 : void nsTableRowGroupFrame::AdjustRowIndices(int32_t aRowIndex,
112 : int32_t anAdjustment)
113 : {
114 0 : for (nsIFrame* rowFrame : mFrames) {
115 0 : if (mozilla::StyleDisplay::TableRow == rowFrame->StyleDisplay()->mDisplay) {
116 0 : int32_t index = ((nsTableRowFrame*)rowFrame)->GetRowIndex();
117 0 : if (index >= aRowIndex)
118 0 : ((nsTableRowFrame *)rowFrame)->SetRowIndex(index+anAdjustment);
119 : }
120 : }
121 0 : }
122 :
123 : int32_t
124 0 : nsTableRowGroupFrame::GetAdjustmentForStoredIndex(int32_t aStoredIndex)
125 : {
126 0 : nsTableFrame* tableFrame = GetTableFrame();
127 0 : return tableFrame->GetAdjustmentForStoredIndex(aStoredIndex);
128 : }
129 :
130 : void
131 0 : nsTableRowGroupFrame::MarkRowsAsDeleted(nsTableRowFrame& aStartRowFrame,
132 : int32_t aNumRowsToDelete)
133 : {
134 0 : nsTableRowFrame* currentRowFrame = &aStartRowFrame;
135 : for (;;) {
136 : // XXXneerja - Instead of calling AddDeletedRowIndex() per row frame
137 : // it is possible to change AddDeleteRowIndex to instead take
138 : // <start row index> and <num of rows to mark for deletion> as arguments.
139 : // The problem that emerges here is mDeletedRowIndexRanges only stores
140 : // disjoint index ranges and since AddDeletedRowIndex() must operate on
141 : // the "stored" index, in some cases it is possible that the range
142 : // of indices to delete becomes overlapping EG: Deleting rows 9 - 11 and
143 : // then from the remaining rows deleting the *new* rows 7 to 20.
144 : // Handling these overlapping ranges is much more complicated to
145 : // implement and so I opted to add the deleted row index of one row at a
146 : // time and maintain the invariant that the range of deleted row indices
147 : // is always disjoint.
148 0 : currentRowFrame->AddDeletedRowIndex();
149 0 : if (--aNumRowsToDelete == 0) {
150 0 : break;
151 : }
152 0 : currentRowFrame = do_QueryFrame(currentRowFrame->GetNextSibling());
153 0 : if (!currentRowFrame) {
154 0 : MOZ_ASSERT_UNREACHABLE("expected another row frame");
155 : break;
156 : }
157 : }
158 0 : }
159 :
160 : void
161 0 : nsTableRowGroupFrame::AddDeletedRowIndex(int32_t aDeletedRowStoredIndex)
162 : {
163 0 : nsTableFrame* tableFrame = GetTableFrame();
164 0 : return tableFrame->AddDeletedRowIndex(aDeletedRowStoredIndex);
165 : }
166 :
167 : nsresult
168 0 : nsTableRowGroupFrame::InitRepeatedFrame(nsTableRowGroupFrame* aHeaderFooterFrame)
169 : {
170 0 : nsTableRowFrame* copyRowFrame = GetFirstRow();
171 0 : nsTableRowFrame* originalRowFrame = aHeaderFooterFrame->GetFirstRow();
172 0 : AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
173 0 : while (copyRowFrame && originalRowFrame) {
174 0 : copyRowFrame->AddStateBits(NS_REPEATED_ROW_OR_ROWGROUP);
175 0 : int rowIndex = originalRowFrame->GetRowIndex();
176 0 : copyRowFrame->SetRowIndex(rowIndex);
177 :
178 : // For each table cell frame set its column index
179 0 : nsTableCellFrame* originalCellFrame = originalRowFrame->GetFirstCell();
180 0 : nsTableCellFrame* copyCellFrame = copyRowFrame->GetFirstCell();
181 0 : while (copyCellFrame && originalCellFrame) {
182 0 : NS_ASSERTION(originalCellFrame->GetContent() == copyCellFrame->GetContent(),
183 : "cell frames have different content");
184 : int32_t colIndex;
185 0 : originalCellFrame->GetColIndex(colIndex);
186 0 : copyCellFrame->SetColIndex(colIndex);
187 :
188 : // Move to the next cell frame
189 0 : copyCellFrame = copyCellFrame->GetNextCell();
190 0 : originalCellFrame = originalCellFrame->GetNextCell();
191 : }
192 :
193 : // Move to the next row frame
194 0 : originalRowFrame = originalRowFrame->GetNextRow();
195 0 : copyRowFrame = copyRowFrame->GetNextRow();
196 : }
197 :
198 0 : return NS_OK;
199 : }
200 :
201 : // Handle the child-traversal part of DisplayGenericTablePart
202 : static void
203 0 : DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
204 : const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
205 : {
206 : nscoord overflowAbove;
207 0 : nsTableRowGroupFrame* f = static_cast<nsTableRowGroupFrame*>(aFrame);
208 : // Don't try to use the row cursor if we have to descend into placeholders;
209 : // we might have rows containing placeholders, where the row's overflow
210 : // area doesn't intersect the dirty rect but we need to descend into the row
211 : // to see out of flows.
212 : // Note that we really want to check ShouldDescendIntoFrame for all
213 : // the rows in |f|, but that's exactly what we're trying to avoid, so we
214 : // approximate it by checking it for |f|: if it's true for any row
215 : // in |f| then it's true for |f| itself.
216 0 : nsIFrame* kid = aBuilder->ShouldDescendIntoFrame(f) ?
217 0 : nullptr : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove);
218 :
219 0 : if (kid) {
220 : // have a cursor, use it
221 0 : while (kid) {
222 0 : if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost() &&
223 0 : kid->GetNormalRect().y - overflowAbove >= aDirtyRect.YMost())
224 0 : break;
225 0 : f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
226 0 : kid = kid->GetNextSibling();
227 : }
228 0 : return;
229 : }
230 :
231 : // No cursor. Traverse children the hard way and build a cursor while we're at it
232 0 : nsTableRowGroupFrame::FrameCursorData* cursor = f->SetupRowCursor();
233 0 : kid = f->PrincipalChildList().FirstChild();
234 0 : while (kid) {
235 0 : f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
236 :
237 0 : if (cursor) {
238 0 : if (!cursor->AppendFrame(kid)) {
239 0 : f->ClearRowCursor();
240 0 : return;
241 : }
242 : }
243 :
244 0 : kid = kid->GetNextSibling();
245 : }
246 0 : if (cursor) {
247 0 : cursor->FinishBuildingCursor();
248 : }
249 : }
250 :
251 : void
252 0 : nsTableRowGroupFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
253 : const nsRect& aDirtyRect,
254 : const nsDisplayListSet& aLists)
255 : {
256 0 : nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect,
257 0 : aLists, DisplayRows);
258 0 : }
259 :
260 : nsIFrame::LogicalSides
261 0 : nsTableRowGroupFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
262 : {
263 0 : if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
264 : StyleBoxDecorationBreak::Clone)) {
265 0 : return LogicalSides();
266 : }
267 :
268 0 : LogicalSides skip;
269 0 : if (nullptr != GetPrevInFlow()) {
270 0 : skip |= eLogicalSideBitsBStart;
271 : }
272 0 : if (nullptr != GetNextInFlow()) {
273 0 : skip |= eLogicalSideBitsBEnd;
274 : }
275 0 : return skip;
276 : }
277 :
278 : // Position and size aKidFrame and update our reflow state.
279 : void
280 0 : nsTableRowGroupFrame::PlaceChild(nsPresContext* aPresContext,
281 : TableRowGroupReflowInput& aReflowInput,
282 : nsIFrame* aKidFrame,
283 : WritingMode aWM,
284 : const LogicalPoint& aKidPosition,
285 : const nsSize& aContainerSize,
286 : ReflowOutput& aDesiredSize,
287 : const nsRect& aOriginalKidRect,
288 : const nsRect& aOriginalKidVisualOverflow)
289 : {
290 0 : bool isFirstReflow = aKidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
291 :
292 : // Place and size the child
293 : FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr,
294 0 : aWM, aKidPosition, aContainerSize, 0);
295 :
296 0 : nsTableFrame* tableFrame = GetTableFrame();
297 0 : if (tableFrame->IsBorderCollapse()) {
298 0 : nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect,
299 0 : aOriginalKidVisualOverflow, isFirstReflow);
300 : }
301 :
302 : // Adjust the running block-offset
303 0 : aReflowInput.bCoord += aDesiredSize.BSize(aWM);
304 :
305 : // If our block-size is constrained then update the available bsize
306 0 : if (NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(aWM)) {
307 0 : aReflowInput.availSize.BSize(aWM) -= aDesiredSize.BSize(aWM);
308 : }
309 0 : }
310 :
311 : void
312 0 : nsTableRowGroupFrame::InitChildReflowInput(nsPresContext& aPresContext,
313 : bool aBorderCollapse,
314 : ReflowInput& aReflowInput)
315 : {
316 0 : nsMargin collapseBorder;
317 0 : nsMargin padding(0,0,0,0);
318 0 : nsMargin* pCollapseBorder = nullptr;
319 0 : if (aBorderCollapse) {
320 0 : nsTableRowFrame *rowFrame = do_QueryFrame(aReflowInput.mFrame);
321 0 : if (rowFrame) {
322 0 : WritingMode wm = GetWritingMode();
323 0 : LogicalMargin border = rowFrame->GetBCBorderWidth(wm);
324 0 : collapseBorder = border.GetPhysicalMargin(wm);
325 0 : pCollapseBorder = &collapseBorder;
326 : }
327 : }
328 0 : aReflowInput.Init(&aPresContext, nullptr, pCollapseBorder, &padding);
329 0 : }
330 :
331 : static void
332 0 : CacheRowBSizesForPrinting(nsPresContext* aPresContext,
333 : nsTableRowFrame* aFirstRow,
334 : WritingMode aWM)
335 : {
336 0 : for (nsTableRowFrame* row = aFirstRow; row; row = row->GetNextRow()) {
337 0 : if (!row->GetPrevInFlow()) {
338 0 : row->SetHasUnpaginatedBSize(true);
339 0 : row->SetUnpaginatedBSize(aPresContext, row->BSize(aWM));
340 : }
341 : }
342 0 : }
343 :
344 : void
345 0 : nsTableRowGroupFrame::ReflowChildren(nsPresContext* aPresContext,
346 : ReflowOutput& aDesiredSize,
347 : TableRowGroupReflowInput& aReflowInput,
348 : nsReflowStatus& aStatus,
349 : bool* aPageBreakBeforeEnd)
350 : {
351 0 : if (aPageBreakBeforeEnd) {
352 0 : *aPageBreakBeforeEnd = false;
353 : }
354 :
355 0 : WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
356 0 : nsTableFrame* tableFrame = GetTableFrame();
357 0 : const bool borderCollapse = tableFrame->IsBorderCollapse();
358 :
359 : // XXXldb Should we really be checking IsPaginated(),
360 : // or should we *only* check available block-size?
361 : // (Think about multi-column layout!)
362 0 : bool isPaginated = aPresContext->IsPaginated() &&
363 0 : NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm);
364 :
365 0 : bool haveRow = false;
366 0 : bool reflowAllKids = aReflowInput.reflowInput.ShouldReflowAllKids() ||
367 0 : tableFrame->IsGeometryDirty();
368 :
369 : // in vertical-rl mode, we always need the row bsizes in order to
370 : // get the necessary containerSize for placing our kids
371 0 : bool needToCalcRowBSizes = reflowAllKids || wm.IsVerticalRL();
372 :
373 : nsSize containerSize =
374 0 : aReflowInput.reflowInput.ComputedSizeAsContainerIfConstrained();
375 :
376 0 : nsIFrame *prevKidFrame = nullptr;
377 0 : for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
378 0 : prevKidFrame = kidFrame, kidFrame = kidFrame->GetNextSibling()) {
379 0 : nsTableRowFrame *rowFrame = do_QueryFrame(kidFrame);
380 0 : if (!rowFrame) {
381 : // XXXldb nsCSSFrameConstructor needs to enforce this!
382 0 : NS_NOTREACHED("yikes, a non-row child");
383 0 : continue;
384 : }
385 0 : nscoord cellSpacingB = tableFrame->GetRowSpacing(rowFrame->GetRowIndex());
386 0 : haveRow = true;
387 :
388 : // Reflow the row frame
389 0 : if (reflowAllKids ||
390 0 : NS_SUBTREE_DIRTY(kidFrame) ||
391 0 : (aReflowInput.reflowInput.mFlags.mSpecialBSizeReflow &&
392 0 : (isPaginated ||
393 0 : kidFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)))) {
394 0 : LogicalRect oldKidRect = kidFrame->GetLogicalRect(wm, containerSize);
395 0 : nsRect oldKidVisualOverflow = kidFrame->GetVisualOverflowRect();
396 :
397 : // XXXldb We used to only pass aDesiredSize.mFlags through for the
398 : // incremental reflow codepath.
399 : ReflowOutput desiredSize(aReflowInput.reflowInput,
400 0 : aDesiredSize.mFlags);
401 0 : desiredSize.ClearSize();
402 :
403 : // Reflow the child into the available space, giving it as much bsize as
404 : // it wants. We'll deal with splitting later after we've computed the row
405 : // bsizes, taking into account cells with row spans...
406 0 : LogicalSize kidAvailSize = aReflowInput.availSize;
407 0 : kidAvailSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
408 : ReflowInput kidReflowInput(aPresContext, aReflowInput.reflowInput,
409 : kidFrame, kidAvailSize,
410 : nullptr,
411 0 : ReflowInput::CALLER_WILL_INIT);
412 0 : InitChildReflowInput(*aPresContext, borderCollapse, kidReflowInput);
413 :
414 : // This can indicate that columns were resized.
415 0 : if (aReflowInput.reflowInput.IsIResize()) {
416 0 : kidReflowInput.SetIResize(true);
417 : }
418 :
419 0 : NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame,
420 : "If we're not on the first frame, we should have a "
421 : "previous sibling...");
422 : // If prev row has nonzero YMost, then we can't be at the top of the page
423 0 : if (prevKidFrame && prevKidFrame->GetNormalRect().YMost() > 0) {
424 0 : kidReflowInput.mFlags.mIsTopOfPage = false;
425 : }
426 :
427 0 : LogicalPoint kidPosition(wm, 0, aReflowInput.bCoord);
428 0 : ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowInput,
429 0 : wm, kidPosition, containerSize, 0, aStatus);
430 0 : kidReflowInput.ApplyRelativePositioning(&kidPosition, containerSize);
431 :
432 : // Place the child
433 : PlaceChild(aPresContext, aReflowInput, kidFrame,
434 : wm, kidPosition, containerSize,
435 0 : desiredSize, oldKidRect.GetPhysicalRect(wm, containerSize),
436 0 : oldKidVisualOverflow);
437 0 : aReflowInput.bCoord += cellSpacingB;
438 :
439 0 : if (!reflowAllKids) {
440 0 : if (IsSimpleRowFrame(aReflowInput.tableFrame, rowFrame)) {
441 : // Inform the row of its new bsize.
442 0 : rowFrame->DidResize();
443 : // the overflow area may have changed inflate the overflow area
444 0 : const nsStylePosition *stylePos = StylePosition();
445 0 : nsStyleUnit unit = stylePos->BSize(wm).GetUnit();
446 0 : if (aReflowInput.tableFrame->IsAutoBSize(wm) &&
447 : unit != eStyleUnit_Coord) {
448 : // Because other cells in the row may need to be aligned
449 : // differently, repaint the entire row
450 0 : InvalidateFrame();
451 0 : } else if (oldKidRect.BSize(wm) != desiredSize.BSize(wm)) {
452 0 : needToCalcRowBSizes = true;
453 : }
454 : } else {
455 0 : needToCalcRowBSizes = true;
456 : }
457 : }
458 :
459 0 : if (isPaginated && aPageBreakBeforeEnd && !*aPageBreakBeforeEnd) {
460 0 : nsTableRowFrame* nextRow = rowFrame->GetNextRow();
461 0 : if (nextRow) {
462 0 : *aPageBreakBeforeEnd = nsTableFrame::PageBreakAfter(kidFrame, nextRow);
463 : }
464 : }
465 : } else {
466 0 : SlideChild(aReflowInput, kidFrame);
467 :
468 : // Adjust the running b-offset so we know where the next row should be placed
469 0 : nscoord bSize = kidFrame->BSize(wm) + cellSpacingB;
470 0 : aReflowInput.bCoord += bSize;
471 :
472 0 : if (NS_UNCONSTRAINEDSIZE != aReflowInput.availSize.BSize(wm)) {
473 0 : aReflowInput.availSize.BSize(wm) -= bSize;
474 : }
475 : }
476 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
477 : }
478 :
479 0 : if (haveRow) {
480 0 : aReflowInput.bCoord -= tableFrame->GetRowSpacing(GetStartRowIndex() +
481 0 : GetRowCount());
482 : }
483 :
484 : // Return our desired rect
485 0 : aDesiredSize.ISize(wm) = aReflowInput.reflowInput.AvailableISize();
486 0 : aDesiredSize.BSize(wm) = aReflowInput.bCoord;
487 :
488 0 : if (aReflowInput.reflowInput.mFlags.mSpecialBSizeReflow) {
489 0 : DidResizeRows(aDesiredSize);
490 0 : if (isPaginated) {
491 0 : CacheRowBSizesForPrinting(aPresContext, GetFirstRow(), wm);
492 : }
493 : }
494 0 : else if (needToCalcRowBSizes) {
495 0 : CalculateRowBSizes(aPresContext, aDesiredSize, aReflowInput.reflowInput);
496 0 : if (!reflowAllKids) {
497 0 : InvalidateFrame();
498 : }
499 : }
500 0 : }
501 :
502 : nsTableRowFrame*
503 0 : nsTableRowGroupFrame::GetFirstRow()
504 : {
505 0 : for (nsIFrame* childFrame : mFrames) {
506 0 : nsTableRowFrame* rowFrame = do_QueryFrame(childFrame);
507 0 : if (rowFrame) {
508 0 : return rowFrame;
509 : }
510 : }
511 0 : return nullptr;
512 : }
513 :
514 : nsTableRowFrame*
515 0 : nsTableRowGroupFrame::GetLastRow()
516 : {
517 0 : for (auto iter = mFrames.rbegin(), end = mFrames.rend(); iter != end; ++iter) {
518 0 : nsTableRowFrame* rowFrame = do_QueryFrame(*iter);
519 0 : if (rowFrame) {
520 0 : return rowFrame;
521 : }
522 : }
523 0 : return nullptr;
524 : }
525 :
526 :
527 : struct RowInfo {
528 0 : RowInfo() { bSize = pctBSize = hasStyleBSize = hasPctBSize = isSpecial = 0; }
529 : unsigned bSize; // content bsize or fixed bsize, excluding pct bsize
530 : unsigned pctBSize:29; // pct bsize
531 : unsigned hasStyleBSize:1;
532 : unsigned hasPctBSize:1;
533 : unsigned isSpecial:1; // there is no cell originating in the row with rowspan=1 and there are at
534 : // least 2 cells spanning the row and there is no style bsize on the row
535 : };
536 :
537 : static void
538 0 : UpdateBSizes(RowInfo& aRowInfo,
539 : nscoord aAdditionalBSize,
540 : nscoord& aTotal,
541 : nscoord& aUnconstrainedTotal)
542 : {
543 0 : aRowInfo.bSize += aAdditionalBSize;
544 0 : aTotal += aAdditionalBSize;
545 0 : if (!aRowInfo.hasStyleBSize) {
546 0 : aUnconstrainedTotal += aAdditionalBSize;
547 : }
548 0 : }
549 :
550 : void
551 0 : nsTableRowGroupFrame::DidResizeRows(ReflowOutput& aDesiredSize)
552 : {
553 : // Update the cells spanning rows with their new bsizes.
554 : // This is the place where all of the cells in the row get set to the bsize
555 : // of the row.
556 : // Reset the overflow area.
557 0 : aDesiredSize.mOverflowAreas.Clear();
558 0 : for (nsTableRowFrame* rowFrame = GetFirstRow();
559 0 : rowFrame; rowFrame = rowFrame->GetNextRow()) {
560 0 : rowFrame->DidResize();
561 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, rowFrame);
562 : }
563 0 : }
564 :
565 : // This calculates the bsize of all the rows and takes into account
566 : // style bsize on the row group, style bsizes on rows and cells, style bsizes on rowspans.
567 : // Actual row bsizes will be adjusted later if the table has a style bsize.
568 : // Even if rows don't change bsize, this method must be called to set the bsizes of each
569 : // cell in the row to the bsize of its row.
570 : void
571 0 : nsTableRowGroupFrame::CalculateRowBSizes(nsPresContext* aPresContext,
572 : ReflowOutput& aDesiredSize,
573 : const ReflowInput& aReflowInput)
574 : {
575 0 : nsTableFrame* tableFrame = GetTableFrame();
576 0 : const bool isPaginated = aPresContext->IsPaginated();
577 :
578 0 : int32_t numEffCols = tableFrame->GetEffectiveColCount();
579 :
580 0 : int32_t startRowIndex = GetStartRowIndex();
581 : // find the row corresponding to the row index we just found
582 0 : nsTableRowFrame* startRowFrame = GetFirstRow();
583 :
584 0 : if (!startRowFrame) {
585 0 : return;
586 : }
587 :
588 : // The current row group block-size is the block-origin of the 1st row
589 : // we are about to calculate a block-size for.
590 0 : WritingMode wm = aReflowInput.GetWritingMode();
591 0 : nsSize containerSize; // actual value is unimportant as we're initially
592 : // computing sizes, not physical positions
593 : nscoord startRowGroupBSize =
594 0 : startRowFrame->GetLogicalNormalPosition(wm, containerSize).B(wm);
595 :
596 0 : int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex());
597 : // Collect the current bsize of each row.
598 0 : if (numRows <= 0)
599 0 : return;
600 :
601 0 : AutoTArray<RowInfo, 32> rowInfo;
602 0 : if (!rowInfo.AppendElements(numRows)) {
603 0 : return;
604 : }
605 :
606 0 : bool hasRowSpanningCell = false;
607 0 : nscoord bSizeOfRows = 0;
608 0 : nscoord bSizeOfUnStyledRows = 0;
609 : // Get the bsize of each row without considering rowspans. This will be the max of
610 : // the largest desired bsize of each cell, the largest style bsize of each cell,
611 : // the style bsize of the row.
612 0 : nscoord pctBSizeBasis = GetBSizeBasis(aReflowInput);
613 : int32_t rowIndex; // the index in rowInfo, not among the rows in the row group
614 : nsTableRowFrame* rowFrame;
615 0 : for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
616 0 : nscoord nonPctBSize = rowFrame->GetContentBSize();
617 0 : if (isPaginated) {
618 0 : nonPctBSize = std::max(nonPctBSize, rowFrame->BSize(wm));
619 : }
620 0 : if (!rowFrame->GetPrevInFlow()) {
621 0 : if (rowFrame->HasPctBSize()) {
622 0 : rowInfo[rowIndex].hasPctBSize = true;
623 0 : rowInfo[rowIndex].pctBSize = rowFrame->GetInitialBSize(pctBSizeBasis);
624 : }
625 0 : rowInfo[rowIndex].hasStyleBSize = rowFrame->HasStyleBSize();
626 0 : nonPctBSize = std::max(nonPctBSize, rowFrame->GetFixedBSize());
627 : }
628 0 : UpdateBSizes(rowInfo[rowIndex], nonPctBSize, bSizeOfRows, bSizeOfUnStyledRows);
629 :
630 0 : if (!rowInfo[rowIndex].hasStyleBSize) {
631 0 : if (isPaginated || tableFrame->HasMoreThanOneCell(rowIndex + startRowIndex)) {
632 0 : rowInfo[rowIndex].isSpecial = true;
633 : // iteratate the row's cell frames to see if any do not have rowspan > 1
634 0 : nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
635 0 : while (cellFrame) {
636 0 : int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
637 0 : if (1 == rowSpan) {
638 0 : rowInfo[rowIndex].isSpecial = false;
639 0 : break;
640 : }
641 0 : cellFrame = cellFrame->GetNextCell();
642 : }
643 : }
644 : }
645 : // See if a cell spans into the row. If so we'll have to do the next step
646 0 : if (!hasRowSpanningCell) {
647 0 : if (tableFrame->RowIsSpannedInto(rowIndex + startRowIndex, numEffCols)) {
648 0 : hasRowSpanningCell = true;
649 : }
650 : }
651 : }
652 :
653 0 : if (hasRowSpanningCell) {
654 : // Get the bsize of cells with rowspans and allocate any extra space to the rows they span
655 : // iteratate the child frames and process the row frames among them
656 0 : for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
657 : // See if the row has an originating cell with rowspan > 1. We cannot determine this for a row in a
658 : // continued row group by calling RowHasSpanningCells, because the row's fif may not have any originating
659 : // cells yet the row may have a continued cell which originates in it.
660 0 : if (GetPrevInFlow() || tableFrame->RowHasSpanningCells(startRowIndex + rowIndex, numEffCols)) {
661 0 : nsTableCellFrame* cellFrame = rowFrame->GetFirstCell();
662 : // iteratate the row's cell frames
663 0 : while (cellFrame) {
664 0 : nscoord cellSpacingB = tableFrame->GetRowSpacing(startRowIndex + rowIndex);
665 0 : int32_t rowSpan = tableFrame->GetEffectiveRowSpan(rowIndex + startRowIndex, *cellFrame);
666 0 : if ((rowIndex + rowSpan) > numRows) {
667 : // there might be rows pushed already to the nextInFlow
668 0 : rowSpan = numRows - rowIndex;
669 : }
670 0 : if (rowSpan > 1) { // a cell with rowspan > 1, determine the bsize of the rows it spans
671 0 : nscoord bsizeOfRowsSpanned = 0;
672 0 : nscoord bsizeOfUnStyledRowsSpanned = 0;
673 0 : nscoord numSpecialRowsSpanned = 0;
674 0 : nscoord cellSpacingTotal = 0;
675 : int32_t spanX;
676 0 : for (spanX = 0; spanX < rowSpan; spanX++) {
677 0 : bsizeOfRowsSpanned += rowInfo[rowIndex + spanX].bSize;
678 0 : if (!rowInfo[rowIndex + spanX].hasStyleBSize) {
679 0 : bsizeOfUnStyledRowsSpanned += rowInfo[rowIndex + spanX].bSize;
680 : }
681 0 : if (0 != spanX) {
682 0 : cellSpacingTotal += cellSpacingB;
683 : }
684 0 : if (rowInfo[rowIndex + spanX].isSpecial) {
685 0 : numSpecialRowsSpanned++;
686 : }
687 : }
688 0 : nscoord bsizeOfAreaSpanned = bsizeOfRowsSpanned + cellSpacingTotal;
689 : // get the bsize of the cell
690 0 : LogicalSize cellFrameSize = cellFrame->GetLogicalSize(wm);
691 0 : LogicalSize cellDesSize = cellFrame->GetDesiredSize();
692 0 : rowFrame->CalculateCellActualBSize(cellFrame, cellDesSize.BSize(wm), wm);
693 0 : cellFrameSize.BSize(wm) = cellDesSize.BSize(wm);
694 0 : if (cellFrame->HasVerticalAlignBaseline()) {
695 : // to ensure that a spanning cell with a long descender doesn't
696 : // collide with the next row, we need to take into account the shift
697 : // that will be done to align the cell on the baseline of the row.
698 0 : cellFrameSize.BSize(wm) += rowFrame->GetMaxCellAscent() -
699 0 : cellFrame->GetCellBaseline();
700 : }
701 :
702 0 : if (bsizeOfAreaSpanned < cellFrameSize.BSize(wm)) {
703 : // the cell's bsize is larger than the available space of the rows it
704 : // spans so distribute the excess bsize to the rows affected
705 0 : nscoord extra = cellFrameSize.BSize(wm) - bsizeOfAreaSpanned;
706 0 : nscoord extraUsed = 0;
707 0 : if (0 == numSpecialRowsSpanned) {
708 : //NS_ASSERTION(bsizeOfRowsSpanned > 0, "invalid row span situation");
709 0 : bool haveUnStyledRowsSpanned = (bsizeOfUnStyledRowsSpanned > 0);
710 : nscoord divisor = (haveUnStyledRowsSpanned)
711 0 : ? bsizeOfUnStyledRowsSpanned : bsizeOfRowsSpanned;
712 0 : if (divisor > 0) {
713 0 : for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
714 0 : if (!haveUnStyledRowsSpanned || !rowInfo[rowIndex + spanX].hasStyleBSize) {
715 : // The amount of additional space each row gets is proportional to its bsize
716 0 : float percent = ((float)rowInfo[rowIndex + spanX].bSize) / ((float)divisor);
717 :
718 : // give rows their percentage, except for the first row which gets the remainder
719 0 : nscoord extraForRow = (0 == spanX) ? extra - extraUsed
720 0 : : NSToCoordRound(((float)(extra)) * percent);
721 0 : extraForRow = std::min(extraForRow, extra - extraUsed);
722 : // update the row bsize
723 0 : UpdateBSizes(rowInfo[rowIndex + spanX], extraForRow, bSizeOfRows, bSizeOfUnStyledRows);
724 0 : extraUsed += extraForRow;
725 0 : if (extraUsed >= extra) {
726 0 : NS_ASSERTION((extraUsed == extra), "invalid row bsize calculation");
727 0 : break;
728 : }
729 : }
730 : }
731 : }
732 : else {
733 : // put everything in the last row
734 0 : UpdateBSizes(rowInfo[rowIndex + rowSpan - 1], extra, bSizeOfRows, bSizeOfUnStyledRows);
735 : }
736 : }
737 : else {
738 : // give the extra to the special rows
739 0 : nscoord numSpecialRowsAllocated = 0;
740 0 : for (spanX = rowSpan - 1; spanX >= 0; spanX--) {
741 0 : if (rowInfo[rowIndex + spanX].isSpecial) {
742 : // The amount of additional space each degenerate row gets is proportional to the number of them
743 0 : float percent = 1.0f / ((float)numSpecialRowsSpanned);
744 :
745 : // give rows their percentage, except for the first row which gets the remainder
746 0 : nscoord extraForRow = (numSpecialRowsSpanned - 1 == numSpecialRowsAllocated)
747 0 : ? extra - extraUsed
748 0 : : NSToCoordRound(((float)(extra)) * percent);
749 0 : extraForRow = std::min(extraForRow, extra - extraUsed);
750 : // update the row bsize
751 0 : UpdateBSizes(rowInfo[rowIndex + spanX], extraForRow, bSizeOfRows, bSizeOfUnStyledRows);
752 0 : extraUsed += extraForRow;
753 0 : if (extraUsed >= extra) {
754 0 : NS_ASSERTION((extraUsed == extra), "invalid row bsize calculation");
755 0 : break;
756 : }
757 : }
758 : }
759 : }
760 : }
761 : } // if (rowSpan > 1)
762 0 : cellFrame = cellFrame->GetNextCell();
763 : } // while (cellFrame)
764 : } // if (tableFrame->RowHasSpanningCells(startRowIndex + rowIndex) {
765 : } // while (rowFrame)
766 : }
767 :
768 : // pct bsize rows have already got their content bsizes.
769 : // Give them their pct bsizes up to pctBSizeBasis
770 0 : nscoord extra = pctBSizeBasis - bSizeOfRows;
771 0 : for (rowFrame = startRowFrame, rowIndex = 0; rowFrame && (extra > 0);
772 : rowFrame = rowFrame->GetNextRow(), rowIndex++) {
773 0 : RowInfo& rInfo = rowInfo[rowIndex];
774 0 : if (rInfo.hasPctBSize) {
775 0 : nscoord rowExtra = (rInfo.pctBSize > rInfo.bSize)
776 0 : ? rInfo.pctBSize - rInfo.bSize: 0;
777 0 : rowExtra = std::min(rowExtra, extra);
778 0 : UpdateBSizes(rInfo, rowExtra, bSizeOfRows, bSizeOfUnStyledRows);
779 0 : extra -= rowExtra;
780 : }
781 : }
782 :
783 0 : bool styleBSizeAllocation = false;
784 0 : nscoord rowGroupBSize = startRowGroupBSize + bSizeOfRows +
785 0 : tableFrame->GetRowSpacing(0, numRows-1);
786 : // if we have a style bsize, allocate the extra bsize to unconstrained rows
787 0 : if ((aReflowInput.ComputedBSize() > rowGroupBSize) &&
788 0 : (NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize())) {
789 0 : nscoord extraComputedBSize = aReflowInput.ComputedBSize() - rowGroupBSize;
790 0 : nscoord extraUsed = 0;
791 0 : bool haveUnStyledRows = (bSizeOfUnStyledRows > 0);
792 : nscoord divisor = (haveUnStyledRows)
793 0 : ? bSizeOfUnStyledRows : bSizeOfRows;
794 0 : if (divisor > 0) {
795 0 : styleBSizeAllocation = true;
796 0 : for (rowIndex = 0; rowIndex < numRows; rowIndex++) {
797 0 : if (!haveUnStyledRows || !rowInfo[rowIndex].hasStyleBSize) {
798 : // The amount of additional space each row gets is based on the
799 : // percentage of space it occupies
800 0 : float percent = ((float)rowInfo[rowIndex].bSize) / ((float)divisor);
801 : // give rows their percentage, except for the last row which gets the remainder
802 0 : nscoord extraForRow = (numRows - 1 == rowIndex)
803 0 : ? extraComputedBSize - extraUsed
804 0 : : NSToCoordRound(((float)extraComputedBSize) * percent);
805 0 : extraForRow = std::min(extraForRow, extraComputedBSize - extraUsed);
806 : // update the row bsize
807 0 : UpdateBSizes(rowInfo[rowIndex], extraForRow, bSizeOfRows, bSizeOfUnStyledRows);
808 0 : extraUsed += extraForRow;
809 0 : if (extraUsed >= extraComputedBSize) {
810 0 : NS_ASSERTION((extraUsed == extraComputedBSize), "invalid row bsize calculation");
811 0 : break;
812 : }
813 : }
814 : }
815 : }
816 0 : rowGroupBSize = aReflowInput.ComputedBSize();
817 : }
818 :
819 0 : if (wm.IsVertical()) {
820 : // we need the correct containerSize below for block positioning in
821 : // vertical-rl writing mode
822 0 : containerSize.width = rowGroupBSize;
823 : }
824 :
825 0 : nscoord bOrigin = startRowGroupBSize;
826 : // update the rows with their (potentially) new bsizes
827 0 : for (rowFrame = startRowFrame, rowIndex = 0; rowFrame;
828 : rowFrame = rowFrame->GetNextRow(), rowIndex++) {
829 0 : nsRect rowBounds = rowFrame->GetRect();
830 0 : LogicalSize rowBoundsSize(wm, rowBounds.Size());
831 0 : nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
832 : nscoord deltaB =
833 0 : bOrigin - rowFrame->GetLogicalNormalPosition(wm, containerSize).B(wm);
834 :
835 0 : nscoord rowBSize = (rowInfo[rowIndex].bSize > 0) ? rowInfo[rowIndex].bSize : 0;
836 :
837 0 : if (deltaB != 0 || (rowBSize != rowBoundsSize.BSize(wm))) {
838 : // Resize/move the row to its final size and position
839 0 : if (deltaB != 0) {
840 0 : rowFrame->InvalidateFrameSubtree();
841 : }
842 :
843 0 : rowFrame->MovePositionBy(wm, LogicalPoint(wm, 0, deltaB));
844 0 : rowFrame->SetSize(LogicalSize(wm, rowBoundsSize.ISize(wm), rowBSize));
845 :
846 : nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow,
847 0 : false);
848 :
849 0 : if (deltaB != 0) {
850 0 : nsTableFrame::RePositionViews(rowFrame);
851 : // XXXbz we don't need to update our overflow area?
852 : }
853 : }
854 0 : bOrigin += rowBSize + tableFrame->GetRowSpacing(startRowIndex + rowIndex);
855 : }
856 :
857 0 : if (isPaginated && styleBSizeAllocation) {
858 : // since the row group has a style bsize, cache the row bsizes,
859 : // so next in flows can honor them
860 0 : CacheRowBSizesForPrinting(aPresContext, GetFirstRow(), wm);
861 : }
862 :
863 0 : DidResizeRows(aDesiredSize);
864 :
865 0 : aDesiredSize.BSize(wm) = rowGroupBSize; // Adjust our desired size
866 : }
867 :
868 : nscoord
869 0 : nsTableRowGroupFrame::CollapseRowGroupIfNecessary(nscoord aBTotalOffset,
870 : nscoord aISize,
871 : WritingMode aWM)
872 : {
873 0 : nsTableFrame* tableFrame = GetTableFrame();
874 0 : nsSize containerSize = tableFrame->GetSize();
875 0 : const nsStyleVisibility* groupVis = StyleVisibility();
876 0 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
877 0 : if (collapseGroup) {
878 0 : tableFrame->SetNeedToCollapse(true);
879 : }
880 :
881 0 : nsOverflowAreas overflow;
882 :
883 0 : nsTableRowFrame* rowFrame = GetFirstRow();
884 0 : bool didCollapse = false;
885 0 : nscoord bGroupOffset = 0;
886 0 : while (rowFrame) {
887 0 : bGroupOffset += rowFrame->CollapseRowIfNecessary(bGroupOffset,
888 : aISize, collapseGroup,
889 : didCollapse);
890 0 : ConsiderChildOverflow(overflow, rowFrame);
891 0 : rowFrame = rowFrame->GetNextRow();
892 : }
893 :
894 0 : LogicalRect groupRect = GetLogicalRect(aWM, containerSize);
895 0 : nsRect oldGroupRect = GetRect();
896 0 : nsRect oldGroupVisualOverflow = GetVisualOverflowRect();
897 :
898 0 : groupRect.BSize(aWM) -= bGroupOffset;
899 0 : if (didCollapse) {
900 : // add back the cellspacing between rowgroups
901 0 : groupRect.BSize(aWM) += tableFrame->GetRowSpacing(GetStartRowIndex() +
902 0 : GetRowCount());
903 : }
904 :
905 0 : groupRect.BStart(aWM) -= aBTotalOffset;
906 0 : groupRect.ISize(aWM) = aISize;
907 :
908 0 : if (aBTotalOffset != 0) {
909 0 : InvalidateFrameSubtree();
910 : }
911 :
912 0 : SetRect(aWM, groupRect, containerSize);
913 0 : overflow.UnionAllWith(nsRect(0, 0, groupRect.Width(aWM),
914 0 : groupRect.Height(aWM)));
915 0 : FinishAndStoreOverflow(overflow, groupRect.Size(aWM).GetPhysicalSize(aWM));
916 0 : nsTableFrame::RePositionViews(this);
917 0 : nsTableFrame::InvalidateTableFrame(this, oldGroupRect, oldGroupVisualOverflow,
918 0 : false);
919 :
920 0 : return bGroupOffset;
921 : }
922 :
923 : // Move a child that was skipped during a reflow.
924 : void
925 0 : nsTableRowGroupFrame::SlideChild(TableRowGroupReflowInput& aReflowInput,
926 : nsIFrame* aKidFrame)
927 : {
928 : // Move the frame if we need to.
929 0 : WritingMode wm = aReflowInput.reflowInput.GetWritingMode();
930 : const nsSize containerSize =
931 0 : aReflowInput.reflowInput.ComputedSizeAsContainerIfConstrained();
932 : LogicalPoint oldPosition =
933 0 : aKidFrame->GetLogicalNormalPosition(wm, containerSize);
934 0 : LogicalPoint newPosition = oldPosition;
935 0 : newPosition.B(wm) = aReflowInput.bCoord;
936 0 : if (oldPosition.B(wm) != newPosition.B(wm)) {
937 0 : aKidFrame->InvalidateFrameSubtree();
938 0 : aReflowInput.reflowInput.ApplyRelativePositioning(&newPosition,
939 0 : containerSize);
940 0 : aKidFrame->SetPosition(wm, newPosition, containerSize);
941 0 : nsTableFrame::RePositionViews(aKidFrame);
942 0 : aKidFrame->InvalidateFrameSubtree();
943 : }
944 0 : }
945 :
946 : // Create a continuing frame, add it to the child list, and then push it
947 : // and the frames that follow
948 : void
949 0 : nsTableRowGroupFrame::CreateContinuingRowFrame(nsPresContext& aPresContext,
950 : nsIFrame& aRowFrame,
951 : nsIFrame** aContRowFrame)
952 : {
953 : // XXX what is the row index?
954 0 : if (!aContRowFrame) {NS_ASSERTION(false, "bad call"); return;}
955 : // create the continuing frame which will create continuing cell frames
956 0 : *aContRowFrame = aPresContext.PresShell()->FrameConstructor()->
957 0 : CreateContinuingFrame(&aPresContext, &aRowFrame, this);
958 :
959 : // Add the continuing row frame to the child list
960 0 : mFrames.InsertFrame(nullptr, &aRowFrame, *aContRowFrame);
961 :
962 : // Push the continuing row frame and the frames that follow
963 0 : PushChildren(*aContRowFrame, &aRowFrame);
964 : }
965 :
966 : // Reflow the cells with rowspan > 1 which originate between aFirstRow
967 : // and end on or after aLastRow. aFirstTruncatedRow is the highest row on the
968 : // page that contains a cell which cannot split on this page
969 : void
970 0 : nsTableRowGroupFrame::SplitSpanningCells(nsPresContext& aPresContext,
971 : const ReflowInput& aReflowInput,
972 : nsTableFrame& aTable,
973 : nsTableRowFrame& aFirstRow,
974 : nsTableRowFrame& aLastRow,
975 : bool aFirstRowIsTopOfPage,
976 : nscoord aSpanningRowBEnd,
977 : nsTableRowFrame*& aContRow,
978 : nsTableRowFrame*& aFirstTruncatedRow,
979 : nscoord& aDesiredBSize)
980 : {
981 0 : NS_ASSERTION(aSpanningRowBEnd >= 0, "Can't split negative bsizes");
982 0 : aFirstTruncatedRow = nullptr;
983 0 : aDesiredBSize = 0;
984 :
985 0 : const bool borderCollapse = aTable.IsBorderCollapse();
986 0 : int32_t lastRowIndex = aLastRow.GetRowIndex();
987 0 : bool wasLast = false;
988 0 : bool haveRowSpan = false;
989 : // Iterate the rows between aFirstRow and aLastRow
990 0 : for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) {
991 0 : wasLast = (row == &aLastRow);
992 0 : int32_t rowIndex = row->GetRowIndex();
993 0 : nsPoint rowPos = row->GetNormalPosition();
994 : // Iterate the cells looking for those that have rowspan > 1
995 0 : for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
996 0 : int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell);
997 : // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow
998 : // were reflowed correctly during the unconstrained bsize reflow.
999 0 : if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) {
1000 0 : haveRowSpan = true;
1001 0 : nsReflowStatus status;
1002 : // Ask the row to reflow the cell to the bsize of all the rows it spans up through aLastRow
1003 : // cellAvailBSize is the space between the row group start and the end of the page
1004 0 : nscoord cellAvailBSize = aSpanningRowBEnd - rowPos.y;
1005 0 : NS_ASSERTION(cellAvailBSize >= 0, "No space for cell?");
1006 0 : bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage;
1007 :
1008 0 : nsRect rowRect = row->GetNormalRect();
1009 : nsSize rowAvailSize(aReflowInput.AvailableWidth(),
1010 0 : std::max(aReflowInput.AvailableHeight() - rowRect.y,
1011 0 : 0));
1012 : // don't let the available height exceed what
1013 : // CalculateRowBSizes set for it
1014 0 : rowAvailSize.height = std::min(rowAvailSize.height, rowRect.height);
1015 : ReflowInput rowReflowInput(&aPresContext, aReflowInput, row,
1016 0 : LogicalSize(row->GetWritingMode(),
1017 : rowAvailSize),
1018 : nullptr,
1019 0 : ReflowInput::CALLER_WILL_INIT);
1020 0 : InitChildReflowInput(aPresContext, borderCollapse, rowReflowInput);
1021 0 : rowReflowInput.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
1022 :
1023 0 : nscoord cellBSize = row->ReflowCellFrame(&aPresContext, rowReflowInput,
1024 : isTopOfPage, cell,
1025 0 : cellAvailBSize, status);
1026 0 : aDesiredBSize = std::max(aDesiredBSize, rowPos.y + cellBSize);
1027 0 : if (status.IsComplete()) {
1028 0 : if (cellBSize > cellAvailBSize) {
1029 0 : aFirstTruncatedRow = row;
1030 0 : if ((row != &aFirstRow) || !aFirstRowIsTopOfPage) {
1031 : // return now, since we will be getting another reflow after either (1) row is
1032 : // moved to the next page or (2) the row group is moved to the next page
1033 0 : return;
1034 : }
1035 : }
1036 : }
1037 : else {
1038 0 : if (!aContRow) {
1039 0 : CreateContinuingRowFrame(aPresContext, aLastRow, (nsIFrame**)&aContRow);
1040 : }
1041 0 : if (aContRow) {
1042 0 : if (row != &aLastRow) {
1043 : // aContRow needs a continuation for cell, since cell spanned into aLastRow
1044 : // but does not originate there
1045 : nsTableCellFrame* contCell = static_cast<nsTableCellFrame*>(
1046 : aPresContext.PresShell()->FrameConstructor()->
1047 0 : CreateContinuingFrame(&aPresContext, cell, &aLastRow));
1048 : int32_t colIndex;
1049 0 : cell->GetColIndex(colIndex);
1050 0 : aContRow->InsertCellFrame(contCell, colIndex);
1051 : }
1052 : }
1053 : }
1054 : }
1055 : }
1056 : }
1057 0 : if (!haveRowSpan) {
1058 0 : aDesiredBSize = aLastRow.GetNormalRect().YMost();
1059 : }
1060 : }
1061 :
1062 : // Remove the next-in-flow of the row, its cells and their cell blocks. This
1063 : // is necessary in case the row doesn't need a continuation later on or needs
1064 : // a continuation which doesn't have the same number of cells that now exist.
1065 : void
1066 0 : nsTableRowGroupFrame::UndoContinuedRow(nsPresContext* aPresContext,
1067 : nsTableRowFrame* aRow)
1068 : {
1069 0 : if (!aRow) return; // allow null aRow to avoid callers doing null checks
1070 :
1071 : // rowBefore was the prev-sibling of aRow's next-sibling before aRow was created
1072 0 : nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
1073 0 : NS_PRECONDITION(mFrames.ContainsFrame(rowBefore),
1074 : "rowBefore not in our frame list?");
1075 :
1076 0 : AutoFrameListPtr overflows(aPresContext, StealOverflowFrames());
1077 0 : if (!rowBefore || !overflows || overflows->IsEmpty() ||
1078 0 : overflows->FirstChild() != aRow) {
1079 0 : NS_ERROR("invalid continued row");
1080 0 : return;
1081 : }
1082 :
1083 : // Destroy aRow, its cells, and their cell blocks. Cell blocks that have split
1084 : // will not have reflowed yet to pick up content from any overflow lines.
1085 0 : overflows->DestroyFrame(aRow);
1086 :
1087 : // Put the overflow rows into our child list
1088 0 : if (!overflows->IsEmpty()) {
1089 0 : mFrames.InsertFrames(nullptr, rowBefore, *overflows);
1090 : }
1091 : }
1092 :
1093 : static nsTableRowFrame*
1094 0 : GetRowBefore(nsTableRowFrame& aStartRow,
1095 : nsTableRowFrame& aRow)
1096 : {
1097 0 : nsTableRowFrame* rowBefore = nullptr;
1098 0 : for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) {
1099 0 : rowBefore = sib;
1100 : }
1101 0 : return rowBefore;
1102 : }
1103 :
1104 : nsresult
1105 0 : nsTableRowGroupFrame::SplitRowGroup(nsPresContext* aPresContext,
1106 : ReflowOutput& aDesiredSize,
1107 : const ReflowInput& aReflowInput,
1108 : nsTableFrame* aTableFrame,
1109 : nsReflowStatus& aStatus,
1110 : bool aRowForcedPageBreak)
1111 : {
1112 0 : NS_PRECONDITION(aPresContext->IsPaginated(), "SplitRowGroup currently supports only paged media");
1113 :
1114 0 : nsTableRowFrame* prevRowFrame = nullptr;
1115 0 : aDesiredSize.Height() = 0;
1116 :
1117 0 : nscoord availWidth = aReflowInput.AvailableWidth();
1118 0 : nscoord availHeight = aReflowInput.AvailableHeight();
1119 :
1120 0 : const bool borderCollapse = aTableFrame->IsBorderCollapse();
1121 :
1122 : // get the page height
1123 0 : nscoord pageHeight = aPresContext->GetPageSize().height;
1124 0 : NS_ASSERTION(pageHeight != NS_UNCONSTRAINEDSIZE,
1125 : "The table shouldn't be split when there should be space");
1126 :
1127 0 : bool isTopOfPage = aReflowInput.mFlags.mIsTopOfPage;
1128 0 : nsTableRowFrame* firstRowThisPage = GetFirstRow();
1129 :
1130 : // Need to dirty the table's geometry, or else the row might skip
1131 : // reflowing its cell as an optimization.
1132 0 : aTableFrame->SetGeometryDirty();
1133 :
1134 : // Walk each of the row frames looking for the first row frame that doesn't fit
1135 : // in the available space
1136 0 : for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) {
1137 0 : bool rowIsOnPage = true;
1138 0 : nscoord cellSpacingB = aTableFrame->GetRowSpacing(rowFrame->GetRowIndex());
1139 0 : nsRect rowRect = rowFrame->GetNormalRect();
1140 : // See if the row fits on this page
1141 0 : if (rowRect.YMost() > availHeight) {
1142 0 : nsTableRowFrame* contRow = nullptr;
1143 : // Reflow the row in the availabe space and have it split if it is the 1st
1144 : // row (on the page) or there is at least 5% of the current page available
1145 : // XXX this 5% should be made a preference
1146 0 : if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) {
1147 0 : nsSize availSize(availWidth, std::max(availHeight - rowRect.y, 0));
1148 : // don't let the available height exceed what CalculateRowHeights set for it
1149 0 : availSize.height = std::min(availSize.height, rowRect.height);
1150 :
1151 : ReflowInput rowReflowInput(aPresContext, aReflowInput, rowFrame,
1152 0 : LogicalSize(rowFrame->GetWritingMode(),
1153 : availSize),
1154 : nullptr,
1155 0 : ReflowInput::CALLER_WILL_INIT);
1156 :
1157 0 : InitChildReflowInput(*aPresContext, borderCollapse, rowReflowInput);
1158 0 : rowReflowInput.mFlags.mIsTopOfPage = isTopOfPage; // set top of page
1159 0 : ReflowOutput rowMetrics(aReflowInput);
1160 :
1161 : // Get the old size before we reflow.
1162 0 : nsRect oldRowRect = rowFrame->GetRect();
1163 0 : nsRect oldRowVisualOverflow = rowFrame->GetVisualOverflowRect();
1164 :
1165 : // Reflow the cell with the constrained height. A cell with rowspan >1 will get this
1166 : // reflow later during SplitSpanningCells.
1167 0 : ReflowChild(rowFrame, aPresContext, rowMetrics, rowReflowInput,
1168 0 : 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
1169 0 : rowFrame->SetSize(nsSize(rowMetrics.Width(), rowMetrics.Height()));
1170 0 : rowFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
1171 0 : rowFrame->DidResize();
1172 :
1173 0 : if (!aRowForcedPageBreak && !aStatus.IsFullyComplete() &&
1174 0 : ShouldAvoidBreakInside(aReflowInput)) {
1175 0 : aStatus.SetInlineLineBreakBeforeAndReset();
1176 0 : break;
1177 : }
1178 :
1179 : nsTableFrame::InvalidateTableFrame(rowFrame, oldRowRect,
1180 : oldRowVisualOverflow,
1181 0 : false);
1182 :
1183 0 : if (aStatus.IsIncomplete()) {
1184 : // The row frame is incomplete and all of the rowspan 1 cells' block frames split
1185 0 : if ((rowMetrics.Height() <= rowReflowInput.AvailableHeight()) || isTopOfPage) {
1186 : // The row stays on this page because either it split ok or we're on the top of page.
1187 : // If top of page and the height exceeded the avail height, then there will be data loss
1188 0 : NS_ASSERTION(rowMetrics.Height() <= rowReflowInput.AvailableHeight(),
1189 : "data loss - incomplete row needed more height than available, on top of page");
1190 0 : CreateContinuingRowFrame(*aPresContext, *rowFrame, (nsIFrame**)&contRow);
1191 0 : if (contRow) {
1192 0 : aDesiredSize.Height() += rowMetrics.Height();
1193 0 : if (prevRowFrame)
1194 0 : aDesiredSize.Height() += cellSpacingB;
1195 : }
1196 0 : else return NS_ERROR_NULL_POINTER;
1197 : }
1198 : else {
1199 : // Put the row on the next page to give it more height
1200 0 : rowIsOnPage = false;
1201 : }
1202 : }
1203 : else {
1204 : // The row frame is complete because either (1) its minimum height is greater than the
1205 : // available height we gave it, or (2) it may have been given a larger height through
1206 : // style than its content, or (3) it contains a rowspan >1 cell which hasn't been
1207 : // reflowed with a constrained height yet (we will find out when SplitSpanningCells is
1208 : // called below)
1209 0 : if (rowMetrics.Height() > availSize.height ||
1210 0 : (aStatus.IsInlineBreakBefore() && !aRowForcedPageBreak)) {
1211 : // cases (1) and (2)
1212 0 : if (isTopOfPage) {
1213 : // We're on top of the page, so keep the row on this page. There will be data loss.
1214 : // Push the row frame that follows
1215 0 : nsTableRowFrame* nextRowFrame = rowFrame->GetNextRow();
1216 0 : if (nextRowFrame) {
1217 0 : aStatus.Reset();
1218 0 : aStatus.SetIncomplete();
1219 : }
1220 0 : aDesiredSize.Height() += rowMetrics.Height();
1221 0 : if (prevRowFrame)
1222 0 : aDesiredSize.Height() += cellSpacingB;
1223 0 : NS_WARNING("data loss - complete row needed more height than available, on top of page");
1224 : }
1225 : else {
1226 : // We're not on top of the page, so put the row on the next page to give it more height
1227 0 : rowIsOnPage = false;
1228 : }
1229 : }
1230 : }
1231 : } //if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20))
1232 : else {
1233 : // put the row on the next page to give it more height
1234 0 : rowIsOnPage = false;
1235 : }
1236 :
1237 0 : nsTableRowFrame* lastRowThisPage = rowFrame;
1238 0 : nscoord spanningRowBottom = availHeight;
1239 0 : if (!rowIsOnPage) {
1240 0 : NS_ASSERTION(!contRow, "We should not have created a continuation if none of this row fits");
1241 0 : if (!aRowForcedPageBreak && ShouldAvoidBreakInside(aReflowInput)) {
1242 0 : aStatus.SetInlineLineBreakBeforeAndReset();
1243 0 : break;
1244 : }
1245 0 : if (prevRowFrame) {
1246 0 : spanningRowBottom = prevRowFrame->GetNormalRect().YMost();
1247 0 : lastRowThisPage = prevRowFrame;
1248 0 : isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowInput.mFlags.mIsTopOfPage;
1249 0 : aStatus.Reset();
1250 0 : aStatus.SetIncomplete();
1251 : }
1252 : else {
1253 : // We can't push children, so let our parent reflow us again with more space
1254 0 : aDesiredSize.Height() = rowRect.YMost();
1255 0 : aStatus.Reset();
1256 0 : break;
1257 : }
1258 : }
1259 : // reflow the cells with rowspan >1 that occur on the page
1260 :
1261 : nsTableRowFrame* firstTruncatedRow;
1262 : nscoord bMost;
1263 0 : SplitSpanningCells(*aPresContext, aReflowInput, *aTableFrame, *firstRowThisPage,
1264 0 : *lastRowThisPage, aReflowInput.mFlags.mIsTopOfPage, spanningRowBottom, contRow,
1265 0 : firstTruncatedRow, bMost);
1266 0 : if (firstTruncatedRow) {
1267 : // A rowspan >1 cell did not fit (and could not split) in the space we gave it
1268 0 : if (firstTruncatedRow == firstRowThisPage) {
1269 0 : if (aReflowInput.mFlags.mIsTopOfPage) {
1270 0 : NS_WARNING("data loss in a row spanned cell");
1271 : }
1272 : else {
1273 : // We can't push children, so let our parent reflow us again with more space
1274 0 : aDesiredSize.Height() = rowRect.YMost();
1275 0 : aStatus.Reset();
1276 0 : UndoContinuedRow(aPresContext, contRow);
1277 0 : contRow = nullptr;
1278 : }
1279 : }
1280 : else { // (firstTruncatedRow != firstRowThisPage)
1281 : // Try to put firstTruncateRow on the next page
1282 0 : nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow);
1283 0 : nscoord oldSpanningRowBottom = spanningRowBottom;
1284 0 : spanningRowBottom = rowBefore->GetNormalRect().YMost();
1285 :
1286 0 : UndoContinuedRow(aPresContext, contRow);
1287 0 : contRow = nullptr;
1288 0 : nsTableRowFrame* oldLastRowThisPage = lastRowThisPage;
1289 0 : lastRowThisPage = rowBefore;
1290 0 : aStatus.Reset();
1291 0 : aStatus.SetIncomplete();
1292 :
1293 : // Call SplitSpanningCells again with rowBefore as the last row on the page
1294 0 : SplitSpanningCells(*aPresContext, aReflowInput, *aTableFrame,
1295 0 : *firstRowThisPage, *rowBefore, aReflowInput.mFlags.mIsTopOfPage,
1296 0 : spanningRowBottom, contRow, firstTruncatedRow, aDesiredSize.Height());
1297 0 : if (firstTruncatedRow) {
1298 0 : if (aReflowInput.mFlags.mIsTopOfPage) {
1299 : // We were better off with the 1st call to SplitSpanningCells, do it again
1300 0 : UndoContinuedRow(aPresContext, contRow);
1301 0 : contRow = nullptr;
1302 0 : lastRowThisPage = oldLastRowThisPage;
1303 0 : spanningRowBottom = oldSpanningRowBottom;
1304 0 : SplitSpanningCells(*aPresContext, aReflowInput, *aTableFrame, *firstRowThisPage,
1305 0 : *lastRowThisPage, aReflowInput.mFlags.mIsTopOfPage, spanningRowBottom, contRow,
1306 0 : firstTruncatedRow, aDesiredSize.Height());
1307 0 : NS_WARNING("data loss in a row spanned cell");
1308 : }
1309 : else {
1310 : // Let our parent reflow us again with more space
1311 0 : aDesiredSize.Height() = rowRect.YMost();
1312 0 : aStatus.Reset();
1313 0 : UndoContinuedRow(aPresContext, contRow);
1314 0 : contRow = nullptr;
1315 : }
1316 : }
1317 : } // if (firstTruncatedRow == firstRowThisPage)
1318 : } // if (firstTruncatedRow)
1319 : else {
1320 0 : aDesiredSize.Height() = std::max(aDesiredSize.Height(), bMost);
1321 0 : if (contRow) {
1322 0 : aStatus.Reset();
1323 0 : aStatus.SetIncomplete();
1324 : }
1325 : }
1326 0 : if (aStatus.IsIncomplete() && !contRow) {
1327 0 : nsTableRowFrame* nextRow = lastRowThisPage->GetNextRow();
1328 0 : if (nextRow) {
1329 0 : PushChildren(nextRow, lastRowThisPage);
1330 : }
1331 : }
1332 0 : break;
1333 : } // if (rowRect.YMost() > availHeight)
1334 : else {
1335 0 : aDesiredSize.Height() = rowRect.YMost();
1336 0 : prevRowFrame = rowFrame;
1337 : // see if there is a page break after the row
1338 0 : nsTableRowFrame* nextRow = rowFrame->GetNextRow();
1339 0 : if (nextRow && nsTableFrame::PageBreakAfter(rowFrame, nextRow)) {
1340 0 : PushChildren(nextRow, rowFrame);
1341 0 : aStatus.Reset();
1342 0 : aStatus.SetIncomplete();
1343 0 : break;
1344 : }
1345 : }
1346 : // after the 1st row that has a height, we can't be on top
1347 : // of the page anymore.
1348 0 : isTopOfPage = isTopOfPage && rowRect.YMost() == 0;
1349 : }
1350 0 : return NS_OK;
1351 : }
1352 :
1353 : /** Layout the entire row group.
1354 : * This method stacks rows vertically according to HTML 4.0 rules.
1355 : * Rows are responsible for layout of their children.
1356 : */
1357 : void
1358 0 : nsTableRowGroupFrame::Reflow(nsPresContext* aPresContext,
1359 : ReflowOutput& aDesiredSize,
1360 : const ReflowInput& aReflowInput,
1361 : nsReflowStatus& aStatus)
1362 : {
1363 0 : MarkInReflow();
1364 0 : DO_GLOBAL_REFLOW_COUNT("nsTableRowGroupFrame");
1365 0 : DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
1366 :
1367 0 : aStatus.Reset();
1368 :
1369 : // Row geometry may be going to change so we need to invalidate any row cursor.
1370 0 : ClearRowCursor();
1371 :
1372 : // see if a special bsize reflow needs to occur due to having a pct bsize
1373 0 : nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);
1374 :
1375 0 : nsTableFrame* tableFrame = GetTableFrame();
1376 0 : TableRowGroupReflowInput state(aReflowInput, tableFrame);
1377 0 : const nsStyleVisibility* groupVis = StyleVisibility();
1378 0 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
1379 0 : if (collapseGroup) {
1380 0 : tableFrame->SetNeedToCollapse(true);
1381 : }
1382 :
1383 : // Check for an overflow list
1384 0 : MoveOverflowToChildList();
1385 :
1386 : // Reflow the existing frames.
1387 0 : bool splitDueToPageBreak = false;
1388 : ReflowChildren(aPresContext, aDesiredSize, state, aStatus,
1389 0 : &splitDueToPageBreak);
1390 :
1391 : // See if all the frames fit. Do not try to split anything if we're
1392 : // not paginated ... we can't split across columns yet.
1393 0 : if (aReflowInput.mFlags.mTableIsSplittable &&
1394 0 : NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableHeight() &&
1395 0 : (aStatus.IsIncomplete() || splitDueToPageBreak ||
1396 0 : aDesiredSize.Height() > aReflowInput.AvailableHeight())) {
1397 : // Nope, find a place to split the row group
1398 0 : bool specialReflow = (bool)aReflowInput.mFlags.mSpecialBSizeReflow;
1399 0 : ((ReflowInput::ReflowInputFlags&)aReflowInput.mFlags).mSpecialBSizeReflow = false;
1400 :
1401 0 : SplitRowGroup(aPresContext, aDesiredSize, aReflowInput, tableFrame, aStatus,
1402 0 : splitDueToPageBreak);
1403 :
1404 0 : ((ReflowInput::ReflowInputFlags&)aReflowInput.mFlags).mSpecialBSizeReflow = specialReflow;
1405 : }
1406 :
1407 : // XXXmats The following is just bogus. We leave it here for now because
1408 : // ReflowChildren should pull up rows from our next-in-flow before returning
1409 : // a Complete status, but doesn't (bug 804888).
1410 0 : if (GetNextInFlow() && GetNextInFlow()->PrincipalChildList().FirstChild()) {
1411 0 : aStatus.SetIncomplete();
1412 : }
1413 :
1414 0 : SetHasStyleBSize((NS_UNCONSTRAINEDSIZE != aReflowInput.ComputedBSize()) &&
1415 0 : (aReflowInput.ComputedBSize() > 0));
1416 :
1417 : // Just set our isize to what was available.
1418 : // The table will calculate the isize and not use our value.
1419 0 : WritingMode wm = aReflowInput.GetWritingMode();
1420 0 : aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
1421 :
1422 0 : aDesiredSize.UnionOverflowAreasWithDesiredBounds();
1423 :
1424 : // If our parent is in initial reflow, it'll handle invalidating our
1425 : // entire overflow rect.
1426 0 : if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
1427 0 : nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
1428 0 : InvalidateFrame();
1429 : }
1430 :
1431 0 : FinishAndStoreOverflow(&aDesiredSize);
1432 :
1433 : // Any absolutely-positioned children will get reflowed in
1434 : // nsFrame::FixupPositionedTableParts in another pass, so propagate our
1435 : // dirtiness to them before our parent clears our dirty bits.
1436 0 : PushDirtyBitToAbsoluteFrames();
1437 :
1438 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
1439 0 : }
1440 :
1441 : bool
1442 0 : nsTableRowGroupFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas)
1443 : {
1444 : // Row cursor invariants depend on the visual overflow area of the rows,
1445 : // which may have changed, so we need to clear the cursor now.
1446 0 : ClearRowCursor();
1447 0 : return nsContainerFrame::ComputeCustomOverflow(aOverflowAreas);
1448 : }
1449 :
1450 : /* virtual */ void
1451 0 : nsTableRowGroupFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
1452 : {
1453 0 : nsContainerFrame::DidSetStyleContext(aOldStyleContext);
1454 :
1455 0 : if (!aOldStyleContext) //avoid this on init
1456 0 : return;
1457 :
1458 0 : nsTableFrame* tableFrame = GetTableFrame();
1459 0 : if (tableFrame->IsBorderCollapse() &&
1460 0 : tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
1461 : TableArea damageArea(0, GetStartRowIndex(), tableFrame->GetColCount(),
1462 0 : GetRowCount());
1463 0 : tableFrame->AddBCDamageArea(damageArea);
1464 : }
1465 : }
1466 :
1467 : void
1468 0 : nsTableRowGroupFrame::AppendFrames(ChildListID aListID,
1469 : nsFrameList& aFrameList)
1470 : {
1471 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
1472 :
1473 0 : DrainSelfOverflowList(); // ensure the last frame is in mFrames
1474 0 : ClearRowCursor();
1475 :
1476 : // collect the new row frames in an array
1477 : // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here.
1478 0 : AutoTArray<nsTableRowFrame*, 8> rows;
1479 0 : for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1480 0 : nsTableRowFrame *rowFrame = do_QueryFrame(e.get());
1481 0 : NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
1482 0 : if (rowFrame) {
1483 0 : NS_ASSERTION(mozilla::StyleDisplay::TableRow ==
1484 : e.get()->StyleDisplay()->mDisplay,
1485 : "wrong display type on rowframe");
1486 0 : rows.AppendElement(rowFrame);
1487 : }
1488 : }
1489 :
1490 0 : int32_t rowIndex = GetRowCount();
1491 : // Append the frames to the sibling chain
1492 0 : mFrames.AppendFrames(nullptr, aFrameList);
1493 :
1494 0 : if (rows.Length() > 0) {
1495 0 : nsTableFrame* tableFrame = GetTableFrame();
1496 0 : tableFrame->AppendRows(this, rowIndex, rows);
1497 0 : PresContext()->PresShell()->
1498 0 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1499 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
1500 0 : tableFrame->SetGeometryDirty();
1501 : }
1502 0 : }
1503 :
1504 : void
1505 0 : nsTableRowGroupFrame::InsertFrames(ChildListID aListID,
1506 : nsIFrame* aPrevFrame,
1507 : nsFrameList& aFrameList)
1508 : {
1509 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
1510 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
1511 : "inserting after sibling frame with different parent");
1512 :
1513 0 : DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
1514 0 : ClearRowCursor();
1515 :
1516 : // collect the new row frames in an array
1517 : // XXXbz why are we doing the QI stuff? There shouldn't be any non-rows here.
1518 0 : nsTableFrame* tableFrame = GetTableFrame();
1519 0 : nsTArray<nsTableRowFrame*> rows;
1520 0 : bool gotFirstRow = false;
1521 0 : for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
1522 0 : nsTableRowFrame *rowFrame = do_QueryFrame(e.get());
1523 0 : NS_ASSERTION(rowFrame, "Unexpected frame; frame constructor screwed up");
1524 0 : if (rowFrame) {
1525 0 : NS_ASSERTION(mozilla::StyleDisplay::TableRow ==
1526 : e.get()->StyleDisplay()->mDisplay,
1527 : "wrong display type on rowframe");
1528 0 : rows.AppendElement(rowFrame);
1529 0 : if (!gotFirstRow) {
1530 0 : rowFrame->SetFirstInserted(true);
1531 0 : gotFirstRow = true;
1532 0 : tableFrame->SetRowInserted(true);
1533 : }
1534 : }
1535 : }
1536 :
1537 0 : int32_t startRowIndex = GetStartRowIndex();
1538 : // Insert the frames in the sibling chain
1539 0 : mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
1540 :
1541 0 : int32_t numRows = rows.Length();
1542 0 : if (numRows > 0) {
1543 0 : nsTableRowFrame* prevRow = (nsTableRowFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, LayoutFrameType::TableRow);
1544 0 : int32_t rowIndex = (prevRow) ? prevRow->GetRowIndex() + 1 : startRowIndex;
1545 0 : tableFrame->InsertRows(this, rows, rowIndex, true);
1546 :
1547 0 : PresContext()->PresShell()->
1548 0 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1549 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
1550 0 : tableFrame->SetGeometryDirty();
1551 : }
1552 0 : }
1553 :
1554 : void
1555 0 : nsTableRowGroupFrame::RemoveFrame(ChildListID aListID,
1556 : nsIFrame* aOldFrame)
1557 : {
1558 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
1559 :
1560 0 : ClearRowCursor();
1561 :
1562 : // XXX why are we doing the QI stuff? There shouldn't be any non-rows here.
1563 0 : nsTableRowFrame* rowFrame = do_QueryFrame(aOldFrame);
1564 0 : if (rowFrame) {
1565 0 : nsTableFrame* tableFrame = GetTableFrame();
1566 : // remove the rows from the table (and flag a rebalance)
1567 0 : tableFrame->RemoveRows(*rowFrame, 1, true);
1568 :
1569 0 : PresContext()->PresShell()->
1570 0 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1571 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
1572 0 : tableFrame->SetGeometryDirty();
1573 : }
1574 0 : mFrames.DestroyFrame(aOldFrame);
1575 0 : }
1576 :
1577 : /* virtual */ nsMargin
1578 0 : nsTableRowGroupFrame::GetUsedMargin() const
1579 : {
1580 0 : return nsMargin(0,0,0,0);
1581 : }
1582 :
1583 : /* virtual */ nsMargin
1584 0 : nsTableRowGroupFrame::GetUsedBorder() const
1585 : {
1586 0 : return nsMargin(0,0,0,0);
1587 : }
1588 :
1589 : /* virtual */ nsMargin
1590 0 : nsTableRowGroupFrame::GetUsedPadding() const
1591 : {
1592 0 : return nsMargin(0,0,0,0);
1593 : }
1594 :
1595 : nscoord
1596 0 : nsTableRowGroupFrame::GetBSizeBasis(const ReflowInput& aReflowInput)
1597 : {
1598 0 : nscoord result = 0;
1599 0 : nsTableFrame* tableFrame = GetTableFrame();
1600 0 : int32_t startRowIndex = GetStartRowIndex();
1601 0 : if ((aReflowInput.ComputedBSize() > 0) && (aReflowInput.ComputedBSize() < NS_UNCONSTRAINEDSIZE)) {
1602 0 : nscoord cellSpacing = tableFrame->GetRowSpacing(startRowIndex,
1603 : std::max(startRowIndex,
1604 0 : startRowIndex + GetRowCount() - 1));
1605 0 : result = aReflowInput.ComputedBSize() - cellSpacing;
1606 : }
1607 : else {
1608 0 : const ReflowInput* parentRI = aReflowInput.mParentReflowInput;
1609 0 : if (parentRI && (tableFrame != parentRI->mFrame)) {
1610 0 : parentRI = parentRI->mParentReflowInput;
1611 : }
1612 0 : if (parentRI && (tableFrame == parentRI->mFrame) &&
1613 0 : (parentRI->ComputedBSize() > 0) && (parentRI->ComputedBSize() < NS_UNCONSTRAINEDSIZE)) {
1614 0 : nscoord cellSpacing = tableFrame->GetRowSpacing(-1, tableFrame->GetRowCount());
1615 0 : result = parentRI->ComputedBSize() - cellSpacing;
1616 : }
1617 : }
1618 :
1619 0 : return result;
1620 : }
1621 :
1622 : bool
1623 0 : nsTableRowGroupFrame::IsSimpleRowFrame(nsTableFrame* aTableFrame,
1624 : nsTableRowFrame* aRowFrame)
1625 : {
1626 0 : int32_t rowIndex = aRowFrame->GetRowIndex();
1627 :
1628 : // It's a simple row frame if there are no cells that span into or
1629 : // across the row
1630 0 : int32_t numEffCols = aTableFrame->GetEffectiveColCount();
1631 0 : if (!aTableFrame->RowIsSpannedInto(rowIndex, numEffCols) &&
1632 0 : !aTableFrame->RowHasSpanningCells(rowIndex, numEffCols)) {
1633 0 : return true;
1634 : }
1635 :
1636 0 : return false;
1637 : }
1638 :
1639 : /** find page break before the first row **/
1640 : bool
1641 0 : nsTableRowGroupFrame::HasInternalBreakBefore() const
1642 : {
1643 0 : nsIFrame* firstChild = mFrames.FirstChild();
1644 0 : if (!firstChild)
1645 0 : return false;
1646 0 : return firstChild->StyleDisplay()->mBreakBefore;
1647 : }
1648 :
1649 : /** find page break after the last row **/
1650 : bool
1651 0 : nsTableRowGroupFrame::HasInternalBreakAfter() const
1652 : {
1653 0 : nsIFrame* lastChild = mFrames.LastChild();
1654 0 : if (!lastChild)
1655 0 : return false;
1656 0 : return lastChild->StyleDisplay()->mBreakAfter;
1657 : }
1658 : /* ----- global methods ----- */
1659 :
1660 : nsTableRowGroupFrame*
1661 0 : NS_NewTableRowGroupFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1662 : {
1663 0 : return new (aPresShell) nsTableRowGroupFrame(aContext);
1664 : }
1665 :
1666 0 : NS_IMPL_FRAMEARENA_HELPERS(nsTableRowGroupFrame)
1667 :
1668 : #ifdef DEBUG_FRAME_DUMP
1669 : nsresult
1670 0 : nsTableRowGroupFrame::GetFrameName(nsAString& aResult) const
1671 : {
1672 0 : return MakeFrameName(NS_LITERAL_STRING("TableRowGroup"), aResult);
1673 : }
1674 : #endif
1675 :
1676 : LogicalMargin
1677 0 : nsTableRowGroupFrame::GetBCBorderWidth(WritingMode aWM)
1678 : {
1679 0 : LogicalMargin border(aWM);
1680 0 : nsTableRowFrame* firstRowFrame = nullptr;
1681 0 : nsTableRowFrame* lastRowFrame = nullptr;
1682 0 : for (nsTableRowFrame* rowFrame = GetFirstRow(); rowFrame; rowFrame = rowFrame->GetNextRow()) {
1683 0 : if (!firstRowFrame) {
1684 0 : firstRowFrame = rowFrame;
1685 : }
1686 0 : lastRowFrame = rowFrame;
1687 : }
1688 0 : if (firstRowFrame) {
1689 0 : border.BStart(aWM) = nsPresContext::
1690 0 : CSSPixelsToAppUnits(firstRowFrame->GetBStartBCBorderWidth());
1691 0 : border.BEnd(aWM) = nsPresContext::
1692 0 : CSSPixelsToAppUnits(lastRowFrame->GetBEndBCBorderWidth());
1693 : }
1694 0 : return border;
1695 : }
1696 :
1697 0 : void nsTableRowGroupFrame::SetContinuousBCBorderWidth(LogicalSide aForSide,
1698 : BCPixelSize aPixelValue)
1699 : {
1700 0 : switch (aForSide) {
1701 : case eLogicalSideIEnd:
1702 0 : mIEndContBorderWidth = aPixelValue;
1703 0 : return;
1704 : case eLogicalSideBEnd:
1705 0 : mBEndContBorderWidth = aPixelValue;
1706 0 : return;
1707 : case eLogicalSideIStart:
1708 0 : mIStartContBorderWidth = aPixelValue;
1709 0 : return;
1710 : default:
1711 0 : NS_ERROR("invalid LogicalSide argument");
1712 : }
1713 : }
1714 :
1715 : //nsILineIterator methods
1716 : int32_t
1717 0 : nsTableRowGroupFrame::GetNumLines()
1718 : {
1719 0 : return GetRowCount();
1720 : }
1721 :
1722 : bool
1723 0 : nsTableRowGroupFrame::GetDirection()
1724 : {
1725 : return (NS_STYLE_DIRECTION_RTL ==
1726 0 : GetTableFrame()->StyleVisibility()->mDirection);
1727 : }
1728 :
1729 : NS_IMETHODIMP
1730 0 : nsTableRowGroupFrame::GetLine(int32_t aLineNumber,
1731 : nsIFrame** aFirstFrameOnLine,
1732 : int32_t* aNumFramesOnLine,
1733 : nsRect& aLineBounds)
1734 : {
1735 0 : NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
1736 0 : NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
1737 :
1738 0 : nsTableFrame* table = GetTableFrame();
1739 0 : nsTableCellMap* cellMap = table->GetCellMap();
1740 :
1741 0 : *aFirstFrameOnLine = nullptr;
1742 0 : *aNumFramesOnLine = 0;
1743 0 : aLineBounds.SetRect(0, 0, 0, 0);
1744 :
1745 0 : if ((aLineNumber < 0) || (aLineNumber >= GetRowCount())) {
1746 0 : return NS_OK;
1747 : }
1748 0 : aLineNumber += GetStartRowIndex();
1749 :
1750 0 : *aNumFramesOnLine = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
1751 0 : if (*aNumFramesOnLine == 0) {
1752 0 : return NS_OK;
1753 : }
1754 0 : int32_t colCount = table->GetColCount();
1755 0 : for (int32_t i = 0; i < colCount; i++) {
1756 0 : CellData* data = cellMap->GetDataAt(aLineNumber, i);
1757 0 : if (data && data->IsOrig()) {
1758 0 : *aFirstFrameOnLine = (nsIFrame*)data->GetCellFrame();
1759 0 : nsIFrame* parent = (*aFirstFrameOnLine)->GetParent();
1760 0 : aLineBounds = parent->GetRect();
1761 0 : return NS_OK;
1762 : }
1763 : }
1764 0 : NS_ERROR("cellmap is lying");
1765 0 : return NS_ERROR_FAILURE;
1766 : }
1767 :
1768 : int32_t
1769 0 : nsTableRowGroupFrame::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
1770 : {
1771 0 : NS_ENSURE_TRUE(aFrame, -1);
1772 :
1773 0 : nsTableRowFrame *rowFrame = do_QueryFrame(aFrame);
1774 0 : NS_ASSERTION(rowFrame, "RowGroup contains a frame that is not a row");
1775 :
1776 0 : int32_t rowIndexInGroup = rowFrame->GetRowIndex() - GetStartRowIndex();
1777 :
1778 0 : return rowIndexInGroup >= aStartLine ? rowIndexInGroup : -1;
1779 : }
1780 :
1781 : NS_IMETHODIMP
1782 0 : nsTableRowGroupFrame::CheckLineOrder(int32_t aLine,
1783 : bool *aIsReordered,
1784 : nsIFrame **aFirstVisual,
1785 : nsIFrame **aLastVisual)
1786 : {
1787 0 : *aIsReordered = false;
1788 0 : *aFirstVisual = nullptr;
1789 0 : *aLastVisual = nullptr;
1790 0 : return NS_OK;
1791 : }
1792 :
1793 : NS_IMETHODIMP
1794 0 : nsTableRowGroupFrame::FindFrameAt(int32_t aLineNumber,
1795 : nsPoint aPos,
1796 : nsIFrame** aFrameFound,
1797 : bool* aPosIsBeforeFirstFrame,
1798 : bool* aPosIsAfterLastFrame)
1799 : {
1800 0 : nsTableFrame* table = GetTableFrame();
1801 0 : nsTableCellMap* cellMap = table->GetCellMap();
1802 :
1803 0 : WritingMode wm = table->GetWritingMode();
1804 0 : nsSize containerSize = table->GetSize();
1805 0 : LogicalPoint pos(wm, aPos, containerSize);
1806 :
1807 0 : *aFrameFound = nullptr;
1808 0 : *aPosIsBeforeFirstFrame = true;
1809 0 : *aPosIsAfterLastFrame = false;
1810 :
1811 0 : aLineNumber += GetStartRowIndex();
1812 0 : int32_t numCells = cellMap->GetNumCellsOriginatingInRow(aLineNumber);
1813 0 : if (numCells == 0) {
1814 0 : return NS_OK;
1815 : }
1816 :
1817 0 : nsIFrame* frame = nullptr;
1818 0 : int32_t colCount = table->GetColCount();
1819 0 : for (int32_t i = 0; i < colCount; i++) {
1820 0 : CellData* data = cellMap->GetDataAt(aLineNumber, i);
1821 0 : if (data && data->IsOrig()) {
1822 0 : frame = (nsIFrame*)data->GetCellFrame();
1823 0 : break;
1824 : }
1825 : }
1826 0 : NS_ASSERTION(frame, "cellmap is lying");
1827 : bool isRTL = (NS_STYLE_DIRECTION_RTL ==
1828 0 : table->StyleVisibility()->mDirection);
1829 :
1830 0 : nsIFrame* closestFromStart = nullptr;
1831 0 : nsIFrame* closestFromEnd = nullptr;
1832 0 : int32_t n = numCells;
1833 0 : nsIFrame* firstFrame = frame;
1834 0 : while (n--) {
1835 0 : LogicalRect rect = frame->GetLogicalRect(wm, containerSize);
1836 0 : if (rect.ISize(wm) > 0) {
1837 : // If pos.I() is inside this frame - this is it
1838 0 : if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) {
1839 0 : closestFromStart = closestFromEnd = frame;
1840 0 : break;
1841 : }
1842 0 : if (rect.IStart(wm) < pos.I(wm)) {
1843 0 : if (!closestFromStart ||
1844 0 : rect.IEnd(wm) > closestFromStart->
1845 0 : GetLogicalRect(wm, containerSize).IEnd(wm))
1846 0 : closestFromStart = frame;
1847 : }
1848 : else {
1849 0 : if (!closestFromEnd ||
1850 0 : rect.IStart(wm) < closestFromEnd->
1851 0 : GetLogicalRect(wm, containerSize).IStart(wm))
1852 0 : closestFromEnd = frame;
1853 : }
1854 : }
1855 0 : frame = frame->GetNextSibling();
1856 : }
1857 0 : if (!closestFromStart && !closestFromEnd) {
1858 : // All frames were zero-width. Just take the first one.
1859 0 : closestFromStart = closestFromEnd = firstFrame;
1860 : }
1861 0 : *aPosIsBeforeFirstFrame = isRTL ? !closestFromEnd : !closestFromStart;
1862 0 : *aPosIsAfterLastFrame = isRTL ? !closestFromStart : !closestFromEnd;
1863 0 : if (closestFromStart == closestFromEnd) {
1864 0 : *aFrameFound = closestFromStart;
1865 : }
1866 0 : else if (!closestFromStart) {
1867 0 : *aFrameFound = closestFromEnd;
1868 : }
1869 0 : else if (!closestFromEnd) {
1870 0 : *aFrameFound = closestFromStart;
1871 : }
1872 : else { // we're between two frames
1873 : nscoord delta =
1874 0 : closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) -
1875 0 : closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm);
1876 0 : if (pos.I(wm) < closestFromStart->
1877 0 : GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) {
1878 0 : *aFrameFound = closestFromStart;
1879 : } else {
1880 0 : *aFrameFound = closestFromEnd;
1881 : }
1882 : }
1883 0 : return NS_OK;
1884 : }
1885 :
1886 : NS_IMETHODIMP
1887 0 : nsTableRowGroupFrame::GetNextSiblingOnLine(nsIFrame*& aFrame,
1888 : int32_t aLineNumber)
1889 : {
1890 0 : NS_ENSURE_ARG_POINTER(aFrame);
1891 0 : aFrame = aFrame->GetNextSibling();
1892 0 : return NS_OK;
1893 : }
1894 :
1895 : //end nsLineIterator methods
1896 :
1897 0 : NS_DECLARE_FRAME_PROPERTY_DELETABLE(RowCursorProperty,
1898 : nsTableRowGroupFrame::FrameCursorData)
1899 :
1900 : void
1901 0 : nsTableRowGroupFrame::ClearRowCursor()
1902 : {
1903 0 : if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
1904 0 : return;
1905 : }
1906 :
1907 0 : RemoveStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
1908 0 : DeleteProperty(RowCursorProperty());
1909 : }
1910 :
1911 : nsTableRowGroupFrame::FrameCursorData*
1912 0 : nsTableRowGroupFrame::SetupRowCursor()
1913 : {
1914 0 : if (HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
1915 : // We already have a valid row cursor. Don't waste time rebuilding it.
1916 0 : return nullptr;
1917 : }
1918 :
1919 0 : nsIFrame* f = mFrames.FirstChild();
1920 : int32_t count;
1921 0 : for (count = 0; f && count < MIN_ROWS_NEEDING_CURSOR; ++count) {
1922 0 : f = f->GetNextSibling();
1923 : }
1924 0 : if (!f) {
1925 : // Less than MIN_ROWS_NEEDING_CURSOR rows, so just don't bother
1926 0 : return nullptr;
1927 : }
1928 :
1929 0 : FrameCursorData* data = new FrameCursorData();
1930 0 : if (!data)
1931 0 : return nullptr;
1932 0 : SetProperty(RowCursorProperty(), data);
1933 0 : AddStateBits(NS_ROWGROUP_HAS_ROW_CURSOR);
1934 0 : return data;
1935 : }
1936 :
1937 : nsIFrame*
1938 0 : nsTableRowGroupFrame::GetFirstRowContaining(nscoord aY, nscoord* aOverflowAbove)
1939 : {
1940 0 : if (!HasAnyStateBits(NS_ROWGROUP_HAS_ROW_CURSOR)) {
1941 0 : return nullptr;
1942 : }
1943 :
1944 0 : FrameCursorData* property = GetProperty(RowCursorProperty());
1945 0 : uint32_t cursorIndex = property->mCursorIndex;
1946 0 : uint32_t frameCount = property->mFrames.Length();
1947 0 : if (cursorIndex >= frameCount)
1948 0 : return nullptr;
1949 0 : nsIFrame* cursorFrame = property->mFrames[cursorIndex];
1950 :
1951 : // The cursor's frame list excludes frames with empty overflow-area, so
1952 : // we don't need to check that here.
1953 :
1954 : // We use property->mOverflowBelow here instead of computing the frame's
1955 : // true overflowArea.YMost(), because it is essential for the thresholds
1956 : // to form a monotonically increasing sequence. Otherwise we would break
1957 : // encountering a row whose overflowArea.YMost() is <= aY but which has
1958 : // a row above it containing cell(s) that span to include aY.
1959 0 : while (cursorIndex > 0 &&
1960 0 : cursorFrame->GetNormalRect().YMost() + property->mOverflowBelow > aY) {
1961 0 : --cursorIndex;
1962 0 : cursorFrame = property->mFrames[cursorIndex];
1963 : }
1964 0 : while (cursorIndex + 1 < frameCount &&
1965 0 : cursorFrame->GetNormalRect().YMost() + property->mOverflowBelow <= aY) {
1966 0 : ++cursorIndex;
1967 0 : cursorFrame = property->mFrames[cursorIndex];
1968 : }
1969 :
1970 0 : property->mCursorIndex = cursorIndex;
1971 0 : *aOverflowAbove = property->mOverflowAbove;
1972 0 : return cursorFrame;
1973 : }
1974 :
1975 : bool
1976 0 : nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame)
1977 : {
1978 : // Relative positioning can cause table parts to move, but we will still paint
1979 : // the backgrounds for the parts under them at their 'normal' position. That
1980 : // means that we must consider the overflow rects at both positions. For
1981 : // example, if we use relative positioning to move a row-spanning cell, we
1982 : // will still paint the row background for that cell at its normal position,
1983 : // which will overflow the row.
1984 : // XXX(seth): This probably isn't correct in the presence of transforms.
1985 0 : nsRect positionedOverflowRect = aFrame->GetVisualOverflowRect();
1986 0 : nsPoint positionedToNormal = aFrame->GetNormalPosition() - aFrame->GetPosition();
1987 0 : nsRect normalOverflowRect = positionedOverflowRect + positionedToNormal;
1988 :
1989 0 : nsRect overflowRect = positionedOverflowRect.Union(normalOverflowRect);
1990 0 : if (overflowRect.IsEmpty())
1991 0 : return true;
1992 0 : nscoord overflowAbove = -overflowRect.y;
1993 0 : nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height;
1994 0 : mOverflowAbove = std::max(mOverflowAbove, overflowAbove);
1995 0 : mOverflowBelow = std::max(mOverflowBelow, overflowBelow);
1996 0 : return mFrames.AppendElement(aFrame) != nullptr;
1997 : }
1998 :
1999 : void
2000 0 : nsTableRowGroupFrame::InvalidateFrame(uint32_t aDisplayItemKey)
2001 : {
2002 0 : nsIFrame::InvalidateFrame(aDisplayItemKey);
2003 0 : GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey);
2004 0 : }
2005 :
2006 : void
2007 0 : nsTableRowGroupFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
2008 : {
2009 0 : nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
2010 : // If we have filters applied that would affects our bounds, then
2011 : // we get an inactive layer created and this is computed
2012 : // within FrameLayerBuilder
2013 0 : GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);
2014 0 : }
|