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