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 :
6 : #include "mozilla/Maybe.h"
7 :
8 : #include "nsTableRowFrame.h"
9 : #include "nsTableRowGroupFrame.h"
10 : #include "nsIPresShell.h"
11 : #include "nsPresContext.h"
12 : #include "nsStyleContext.h"
13 : #include "nsStyleConsts.h"
14 : #include "nsGkAtoms.h"
15 : #include "nsIContent.h"
16 : #include "nsTableFrame.h"
17 : #include "nsTableCellFrame.h"
18 : #include "nsCSSRendering.h"
19 : #include "nsHTMLParts.h"
20 : #include "nsTableColGroupFrame.h"
21 : #include "nsTableColFrame.h"
22 : #include "nsCOMPtr.h"
23 : #include "nsDisplayList.h"
24 : #include "nsIFrameInlines.h"
25 : #include <algorithm>
26 :
27 : using namespace mozilla;
28 :
29 : namespace mozilla {
30 :
31 : struct TableCellReflowInput : public ReflowInput
32 : {
33 0 : TableCellReflowInput(nsPresContext* aPresContext,
34 : const ReflowInput& aParentReflowInput,
35 : nsIFrame* aFrame,
36 : const LogicalSize& aAvailableSpace,
37 : uint32_t aFlags = 0)
38 0 : : ReflowInput(aPresContext, aParentReflowInput, aFrame,
39 0 : aAvailableSpace, nullptr, aFlags)
40 : {
41 0 : }
42 :
43 : void FixUp(const LogicalSize& aAvailSpace);
44 : };
45 :
46 : } // namespace mozilla
47 :
48 0 : void TableCellReflowInput::FixUp(const LogicalSize& aAvailSpace)
49 : {
50 : // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base
51 0 : NS_WARNING_ASSERTION(
52 : NS_UNCONSTRAINEDSIZE != aAvailSpace.ISize(mWritingMode),
53 : "have unconstrained inline-size; this should only result from very large "
54 : "sizes, not attempts at intrinsic inline size calculation");
55 0 : if (NS_UNCONSTRAINEDSIZE != ComputedISize()) {
56 0 : nscoord computedISize = aAvailSpace.ISize(mWritingMode) -
57 0 : ComputedLogicalBorderPadding().IStartEnd(mWritingMode);
58 0 : computedISize = std::max(0, computedISize);
59 0 : SetComputedISize(computedISize);
60 : }
61 0 : if (NS_UNCONSTRAINEDSIZE != ComputedBSize() &&
62 0 : NS_UNCONSTRAINEDSIZE != aAvailSpace.BSize(mWritingMode)) {
63 0 : nscoord computedBSize = aAvailSpace.BSize(mWritingMode) -
64 0 : ComputedLogicalBorderPadding().BStartEnd(mWritingMode);
65 0 : computedBSize = std::max(0, computedBSize);
66 0 : SetComputedBSize(computedBSize);
67 : }
68 0 : }
69 :
70 : void
71 0 : nsTableRowFrame::InitChildReflowInput(nsPresContext& aPresContext,
72 : const LogicalSize& aAvailSize,
73 : bool aBorderCollapse,
74 : TableCellReflowInput& aReflowInput)
75 : {
76 0 : nsMargin collapseBorder;
77 0 : nsMargin* pCollapseBorder = nullptr;
78 0 : if (aBorderCollapse) {
79 : // we only reflow cells, so don't need to check frame type
80 0 : nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowInput.mFrame;
81 0 : if (bcCellFrame) {
82 0 : WritingMode wm = GetWritingMode();
83 0 : collapseBorder = bcCellFrame->GetBorderWidth(wm).GetPhysicalMargin(wm);
84 0 : pCollapseBorder = &collapseBorder;
85 : }
86 : }
87 0 : aReflowInput.Init(&aPresContext, nullptr, pCollapseBorder);
88 0 : aReflowInput.FixUp(aAvailSize);
89 0 : }
90 :
91 : void
92 0 : nsTableRowFrame::SetFixedBSize(nscoord aValue)
93 : {
94 0 : nscoord bsize = std::max(0, aValue);
95 0 : if (HasFixedBSize()) {
96 0 : if (bsize > mStyleFixedBSize) {
97 0 : mStyleFixedBSize = bsize;
98 : }
99 : }
100 : else {
101 0 : mStyleFixedBSize = bsize;
102 0 : if (bsize > 0) {
103 0 : SetHasFixedBSize(true);
104 : }
105 : }
106 0 : }
107 :
108 : void
109 0 : nsTableRowFrame::SetPctBSize(float aPctValue,
110 : bool aForce)
111 : {
112 0 : nscoord bsize = std::max(0, NSToCoordRound(aPctValue * 100.0f));
113 0 : if (HasPctBSize()) {
114 0 : if ((bsize > mStylePctBSize) || aForce) {
115 0 : mStylePctBSize = bsize;
116 : }
117 : }
118 : else {
119 0 : mStylePctBSize = bsize;
120 0 : if (bsize > 0) {
121 0 : SetHasPctBSize(true);
122 : }
123 : }
124 0 : }
125 :
126 : /* ----------- nsTableRowFrame ---------- */
127 :
128 0 : NS_QUERYFRAME_HEAD(nsTableRowFrame)
129 0 : NS_QUERYFRAME_ENTRY(nsTableRowFrame)
130 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
131 :
132 0 : nsTableRowFrame::nsTableRowFrame(nsStyleContext* aContext, ClassID aID)
133 : : nsContainerFrame(aContext, aID)
134 : , mContentBSize(0)
135 : , mStylePctBSize(0)
136 : , mStyleFixedBSize(0)
137 : , mMaxCellAscent(0)
138 : , mMaxCellDescent(0)
139 : , mBStartBorderWidth(0)
140 : , mBEndBorderWidth(0)
141 : , mIEndContBorderWidth(0)
142 : , mBStartContBorderWidth(0)
143 0 : , mIStartContBorderWidth(0)
144 : {
145 0 : mBits.mRowIndex = 0;
146 0 : mBits.mHasFixedBSize = 0;
147 0 : mBits.mHasPctBSize = 0;
148 0 : mBits.mFirstInserted = 0;
149 0 : ResetBSize(0);
150 0 : }
151 :
152 0 : nsTableRowFrame::~nsTableRowFrame()
153 : {
154 0 : }
155 :
156 : void
157 0 : nsTableRowFrame::Init(nsIContent* aContent,
158 : nsContainerFrame* aParent,
159 : nsIFrame* aPrevInFlow)
160 : {
161 : // Let the base class do its initialization
162 0 : nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
163 :
164 0 : NS_ASSERTION(mozilla::StyleDisplay::TableRow == StyleDisplay()->mDisplay,
165 : "wrong display on table row frame");
166 :
167 0 : if (aPrevInFlow) {
168 : // Set the row index
169 0 : nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
170 :
171 0 : SetRowIndex(rowFrame->GetRowIndex());
172 : } else {
173 0 : mWritingMode = GetTableFrame()->GetWritingMode();
174 : }
175 0 : }
176 :
177 : void
178 0 : nsTableRowFrame::DestroyFrom(nsIFrame* aDestructRoot)
179 : {
180 0 : if (HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
181 0 : nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
182 : }
183 :
184 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
185 0 : }
186 :
187 : /* virtual */ void
188 0 : nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
189 : {
190 0 : nsContainerFrame::DidSetStyleContext(aOldStyleContext);
191 :
192 0 : if (!aOldStyleContext) //avoid this on init
193 0 : return;
194 :
195 0 : nsTableFrame* tableFrame = GetTableFrame();
196 0 : if (tableFrame->IsBorderCollapse() &&
197 0 : tableFrame->BCRecalcNeeded(aOldStyleContext, StyleContext())) {
198 0 : TableArea damageArea(0, GetRowIndex(), tableFrame->GetColCount(), 1);
199 0 : tableFrame->AddBCDamageArea(damageArea);
200 : }
201 : }
202 :
203 : void
204 0 : nsTableRowFrame::AppendFrames(ChildListID aListID,
205 : nsFrameList& aFrameList)
206 : {
207 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
208 :
209 0 : DrainSelfOverflowList(); // ensure the last frame is in mFrames
210 0 : const nsFrameList::Slice& newCells = mFrames.AppendFrames(nullptr, aFrameList);
211 :
212 : // Add the new cell frames to the table
213 0 : nsTableFrame* tableFrame = GetTableFrame();
214 0 : for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) {
215 0 : nsIFrame *childFrame = e.get();
216 0 : NS_ASSERTION(IS_TABLE_CELL(childFrame->Type()),
217 : "Not a table cell frame/pseudo frame construction failure");
218 0 : tableFrame->AppendCell(static_cast<nsTableCellFrame&>(*childFrame), GetRowIndex());
219 : }
220 :
221 0 : PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
222 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
223 0 : tableFrame->SetGeometryDirty();
224 0 : }
225 :
226 :
227 : void
228 0 : nsTableRowFrame::InsertFrames(ChildListID aListID,
229 : nsIFrame* aPrevFrame,
230 : nsFrameList& aFrameList)
231 : {
232 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
233 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
234 : "inserting after sibling frame with different parent");
235 0 : DrainSelfOverflowList(); // ensure aPrevFrame is in mFrames
236 : //Insert Frames in the frame list
237 0 : const nsFrameList::Slice& newCells = mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
238 :
239 : // Get the table frame
240 0 : nsTableFrame* tableFrame = GetTableFrame();
241 0 : LayoutFrameType cellFrameType = tableFrame->IsBorderCollapse()
242 0 : ? LayoutFrameType::BCTableCell : LayoutFrameType::TableCell;
243 0 : nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, cellFrameType);
244 0 : nsTArray<nsTableCellFrame*> cellChildren;
245 0 : for (nsFrameList::Enumerator e(newCells); !e.AtEnd(); e.Next()) {
246 0 : nsIFrame *childFrame = e.get();
247 0 : NS_ASSERTION(IS_TABLE_CELL(childFrame->Type()),
248 : "Not a table cell frame/pseudo frame construction failure");
249 0 : cellChildren.AppendElement(static_cast<nsTableCellFrame*>(childFrame));
250 : }
251 : // insert the cells into the cell map
252 0 : int32_t colIndex = -1;
253 0 : if (prevCellFrame) {
254 0 : prevCellFrame->GetColIndex(colIndex);
255 : }
256 0 : tableFrame->InsertCells(cellChildren, GetRowIndex(), colIndex);
257 :
258 0 : PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
259 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
260 0 : tableFrame->SetGeometryDirty();
261 0 : }
262 :
263 : void
264 0 : nsTableRowFrame::RemoveFrame(ChildListID aListID,
265 : nsIFrame* aOldFrame)
266 : {
267 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
268 :
269 0 : MOZ_ASSERT((nsTableCellFrame*)do_QueryFrame(aOldFrame));
270 0 : nsTableCellFrame* cellFrame = static_cast<nsTableCellFrame*>(aOldFrame);
271 : // remove the cell from the cell map
272 0 : nsTableFrame* tableFrame = GetTableFrame();
273 0 : tableFrame->RemoveCell(cellFrame, GetRowIndex());
274 :
275 : // Remove the frame and destroy it
276 0 : mFrames.DestroyFrame(aOldFrame);
277 :
278 0 : PresContext()->PresShell()->
279 0 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
280 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
281 :
282 0 : tableFrame->SetGeometryDirty();
283 0 : }
284 :
285 : /* virtual */ nsMargin
286 0 : nsTableRowFrame::GetUsedMargin() const
287 : {
288 0 : return nsMargin(0,0,0,0);
289 : }
290 :
291 : /* virtual */ nsMargin
292 0 : nsTableRowFrame::GetUsedBorder() const
293 : {
294 0 : return nsMargin(0,0,0,0);
295 : }
296 :
297 : /* virtual */ nsMargin
298 0 : nsTableRowFrame::GetUsedPadding() const
299 : {
300 0 : return nsMargin(0,0,0,0);
301 : }
302 :
303 : nscoord
304 0 : GetBSizeOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame,
305 : nsTableFrame& aTableFrame,
306 : const WritingMode aWM)
307 : {
308 0 : nscoord bsize = 0;
309 0 : int32_t rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame);
310 : // add in bsize of rows spanned beyond the 1st one
311 0 : nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling();
312 0 : for (int32_t rowX = 1; ((rowX < rowSpan) && nextRow);) {
313 0 : if (nextRow->IsTableRowFrame()) {
314 0 : bsize += nextRow->BSize(aWM);
315 0 : rowX++;
316 : }
317 0 : bsize += aTableFrame.GetRowSpacing(rowX);
318 0 : nextRow = nextRow->GetNextSibling();
319 : }
320 0 : return bsize;
321 : }
322 :
323 : nsTableCellFrame*
324 0 : nsTableRowFrame::GetFirstCell()
325 : {
326 0 : for (nsIFrame* childFrame : mFrames) {
327 0 : nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
328 0 : if (cellFrame) {
329 0 : return cellFrame;
330 : }
331 : }
332 0 : return nullptr;
333 : }
334 :
335 : /**
336 : * Post-reflow hook. This is where the table row does its post-processing
337 : */
338 : void
339 0 : nsTableRowFrame::DidResize()
340 : {
341 : // Resize and re-align the cell frames based on our row bsize
342 0 : nsTableFrame* tableFrame = GetTableFrame();
343 :
344 0 : WritingMode wm = GetWritingMode();
345 0 : ReflowOutput desiredSize(wm);
346 0 : desiredSize.SetSize(wm, GetLogicalSize(wm));
347 0 : desiredSize.SetOverflowAreasToDesiredBounds();
348 :
349 0 : nsSize containerSize = mRect.Size();
350 :
351 0 : for (nsIFrame* childFrame : mFrames) {
352 0 : nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
353 0 : if (cellFrame) {
354 0 : nscoord cellBSize = BSize(wm) +
355 0 : GetBSizeOfRowsSpannedBelowFirst(*cellFrame, *tableFrame, wm);
356 :
357 : // If the bsize for the cell has changed, we need to reset it;
358 : // and in vertical-rl mode, we need to update the cell's block position
359 : // to account for the containerSize, which may not have been known
360 : // earlier, so we always apply it here.
361 0 : LogicalSize cellSize = cellFrame->GetLogicalSize(wm);
362 0 : if (cellSize.BSize(wm) != cellBSize || wm.IsVerticalRL()) {
363 0 : nsRect cellOldRect = cellFrame->GetRect();
364 0 : nsRect cellVisualOverflow = cellFrame->GetVisualOverflowRect();
365 :
366 0 : if (wm.IsVerticalRL()) {
367 : // Get the old position of the cell, as we want to preserve its
368 : // inline coordinate.
369 : LogicalPoint oldPos =
370 0 : cellFrame->GetLogicalPosition(wm, containerSize);
371 :
372 : // The cell should normally be aligned with the row's block-start,
373 : // so set the B component of the position to zero:
374 0 : LogicalPoint newPos(wm, oldPos.I(wm), 0);
375 :
376 : // ...unless relative positioning is in effect, in which case the
377 : // cell may have been moved away from the row's block-start
378 0 : if (cellFrame->IsRelativelyPositioned()) {
379 : // Find out where the cell would have been without relative
380 : // positioning.
381 : LogicalPoint oldNormalPos =
382 0 : cellFrame->GetLogicalNormalPosition(wm, containerSize);
383 : // The difference (if any) between oldPos and oldNormalPos reflects
384 : // relative positioning that was applied to the cell, and which we
385 : // need to incorporate when resetting the position.
386 0 : newPos.B(wm) = oldPos.B(wm) - oldNormalPos.B(wm);
387 : }
388 :
389 0 : if (oldPos != newPos) {
390 0 : cellFrame->SetPosition(wm, newPos, containerSize);
391 0 : nsTableFrame::RePositionViews(cellFrame);
392 : }
393 : }
394 :
395 0 : cellSize.BSize(wm) = cellBSize;
396 0 : cellFrame->SetSize(wm, cellSize);
397 :
398 0 : nsTableFrame* tableFrame = GetTableFrame();
399 0 : if (tableFrame->IsBorderCollapse()) {
400 : nsTableFrame::InvalidateTableFrame(cellFrame, cellOldRect,
401 : cellVisualOverflow,
402 0 : false);
403 : }
404 : }
405 :
406 : // realign cell content based on the new bsize. We might be able to
407 : // skip this if the bsize didn't change... maybe. Hard to tell.
408 0 : cellFrame->BlockDirAlignChild(wm, mMaxCellAscent);
409 :
410 : // Always store the overflow, even if the height didn't change, since
411 : // we'll lose part of our overflow area otherwise.
412 0 : ConsiderChildOverflow(desiredSize.mOverflowAreas, cellFrame);
413 :
414 : // Note that if the cell's *content* needs to change in response
415 : // to this height, it will get a special bsize reflow.
416 : }
417 : }
418 0 : FinishAndStoreOverflow(&desiredSize);
419 0 : if (HasView()) {
420 0 : nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
421 0 : desiredSize.VisualOverflow(), 0);
422 : }
423 : // Let our base class do the usual work
424 0 : }
425 :
426 : // returns max-ascent amongst all cells that have 'vertical-align: baseline'
427 : // *including* cells with rowspans
428 0 : nscoord nsTableRowFrame::GetMaxCellAscent() const
429 : {
430 0 : return mMaxCellAscent;
431 : }
432 :
433 0 : nscoord nsTableRowFrame::GetRowBaseline(WritingMode aWM)
434 : {
435 0 : if (mMaxCellAscent) {
436 0 : return mMaxCellAscent;
437 : }
438 :
439 : // If we don't have a baseline on any of the cells we go for the lowest
440 : // content edge of the inner block frames.
441 : // Every table cell has a cell frame with its border and padding. Inside
442 : // the cell is a block frame. The cell is as high as the tallest cell in
443 : // the parent row. As a consequence the block frame might not touch both
444 : // the top and the bottom padding of it parent cell frame at the same time.
445 : //
446 : // bbbbbbbbbbbbbbbbbb cell border: b
447 : // bppppppppppppppppb cell padding: p
448 : // bpxxxxxxxxxxxxxxpb inner block: x
449 : // bpx xpb
450 : // bpx xpb
451 : // bpx xpb
452 : // bpxxxxxxxxxxxxxxpb base line
453 : // bp pb
454 : // bp pb
455 : // bppppppppppppppppb
456 : // bbbbbbbbbbbbbbbbbb
457 :
458 0 : nscoord ascent = 0;
459 0 : nsSize containerSize = GetSize();
460 0 : for (nsIFrame* childFrame : mFrames) {
461 0 : if (IS_TABLE_CELL(childFrame->Type())) {
462 0 : nsIFrame* firstKid = childFrame->PrincipalChildList().FirstChild();
463 0 : ascent = std::max(ascent,
464 0 : LogicalRect(aWM, firstKid->GetNormalRect(),
465 0 : containerSize).BEnd(aWM));
466 : }
467 : }
468 0 : return ascent;
469 : }
470 :
471 : nscoord
472 0 : nsTableRowFrame::GetInitialBSize(nscoord aPctBasis) const
473 : {
474 0 : nscoord bsize = 0;
475 0 : if ((aPctBasis > 0) && HasPctBSize()) {
476 0 : bsize = NSToCoordRound(GetPctBSize() * (float)aPctBasis);
477 : }
478 0 : if (HasFixedBSize()) {
479 0 : bsize = std::max(bsize, GetFixedBSize());
480 : }
481 0 : return std::max(bsize, GetContentBSize());
482 : }
483 :
484 : void
485 0 : nsTableRowFrame::ResetBSize(nscoord aFixedBSize)
486 : {
487 0 : SetHasFixedBSize(false);
488 0 : SetHasPctBSize(false);
489 0 : SetFixedBSize(0);
490 0 : SetPctBSize(0);
491 0 : SetContentBSize(0);
492 :
493 0 : if (aFixedBSize > 0) {
494 0 : SetFixedBSize(aFixedBSize);
495 : }
496 :
497 0 : mMaxCellAscent = 0;
498 0 : mMaxCellDescent = 0;
499 0 : }
500 :
501 : void
502 0 : nsTableRowFrame::UpdateBSize(nscoord aBSize,
503 : nscoord aAscent,
504 : nscoord aDescent,
505 : nsTableFrame* aTableFrame,
506 : nsTableCellFrame* aCellFrame)
507 : {
508 0 : if (!aTableFrame || !aCellFrame) {
509 0 : NS_ASSERTION(false , "invalid call");
510 0 : return;
511 : }
512 :
513 0 : if (aBSize != NS_UNCONSTRAINEDSIZE) {
514 0 : if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters
515 0 : if (GetInitialBSize() < aBSize) {
516 0 : int32_t rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
517 0 : if (rowSpan == 1) {
518 0 : SetContentBSize(aBSize);
519 : }
520 : }
521 : }
522 : else { // the alignment on the baseline can change the bsize
523 0 : NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) &&
524 : (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call");
525 : // see if this is a long ascender
526 0 : if (mMaxCellAscent < aAscent) {
527 0 : mMaxCellAscent = aAscent;
528 : }
529 : // see if this is a long descender and without rowspan
530 0 : if (mMaxCellDescent < aDescent) {
531 0 : int32_t rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
532 0 : if (rowSpan == 1) {
533 0 : mMaxCellDescent = aDescent;
534 : }
535 : }
536 : // keep the tallest bsize in sync
537 0 : if (GetInitialBSize() < mMaxCellAscent + mMaxCellDescent) {
538 0 : SetContentBSize(mMaxCellAscent + mMaxCellDescent);
539 : }
540 : }
541 : }
542 : }
543 :
544 : nscoord
545 0 : nsTableRowFrame::CalcBSize(const ReflowInput& aReflowInput)
546 : {
547 0 : nsTableFrame* tableFrame = GetTableFrame();
548 0 : nscoord computedBSize = (NS_UNCONSTRAINEDSIZE == aReflowInput.ComputedBSize())
549 0 : ? 0 : aReflowInput.ComputedBSize();
550 0 : ResetBSize(computedBSize);
551 :
552 0 : WritingMode wm = aReflowInput.GetWritingMode();
553 0 : const nsStylePosition* position = StylePosition();
554 0 : const nsStyleCoord& bsizeStyleCoord = position->BSize(wm);
555 0 : if (bsizeStyleCoord.ConvertsToLength()) {
556 0 : SetFixedBSize(nsRuleNode::ComputeCoordPercentCalc(bsizeStyleCoord, 0));
557 : }
558 0 : else if (eStyleUnit_Percent == bsizeStyleCoord.GetUnit()) {
559 0 : SetPctBSize(bsizeStyleCoord.GetPercentValue());
560 : }
561 : // calc() with percentages is treated like 'auto' on table rows.
562 :
563 0 : for (nsIFrame* kidFrame : mFrames) {
564 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
565 0 : if (cellFrame) {
566 0 : MOZ_ASSERT(cellFrame->GetWritingMode() == wm);
567 0 : LogicalSize desSize = cellFrame->GetDesiredSize();
568 0 : if ((NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) && !GetPrevInFlow()) {
569 0 : CalculateCellActualBSize(cellFrame, desSize.BSize(wm), wm);
570 : }
571 : // bsize may have changed, adjust descent to absorb any excess difference
572 : nscoord ascent;
573 0 : if (!kidFrame->PrincipalChildList().FirstChild()->PrincipalChildList().FirstChild())
574 0 : ascent = desSize.BSize(wm);
575 : else
576 0 : ascent = cellFrame->GetCellBaseline();
577 0 : nscoord descent = desSize.BSize(wm) - ascent;
578 0 : UpdateBSize(desSize.BSize(wm), ascent, descent, tableFrame, cellFrame);
579 : }
580 : }
581 0 : return GetInitialBSize();
582 : }
583 :
584 : void
585 0 : nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
586 : const nsRect& aDirtyRect,
587 : const nsDisplayListSet& aLists)
588 : {
589 0 : nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists);
590 0 : }
591 :
592 : nsIFrame::LogicalSides
593 0 : nsTableRowFrame::GetLogicalSkipSides(const ReflowInput* aReflowInput) const
594 : {
595 0 : if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
596 : StyleBoxDecorationBreak::Clone)) {
597 0 : return LogicalSides();
598 : }
599 :
600 0 : LogicalSides skip;
601 0 : if (nullptr != GetPrevInFlow()) {
602 0 : skip |= eLogicalSideBitsBStart;
603 : }
604 0 : if (nullptr != GetNextInFlow()) {
605 0 : skip |= eLogicalSideBitsBEnd;
606 : }
607 0 : return skip;
608 : }
609 :
610 : // Calculate the cell's actual bsize given its pass2 bsize.
611 : // Takes into account the specified bsize (in the style).
612 : // Modifies the desired bsize that is passed in.
613 : nsresult
614 0 : nsTableRowFrame::CalculateCellActualBSize(nsTableCellFrame* aCellFrame,
615 : nscoord& aDesiredBSize,
616 : WritingMode aWM)
617 : {
618 0 : nscoord specifiedBSize = 0;
619 :
620 : // Get the bsize specified in the style information
621 0 : const nsStylePosition* position = aCellFrame->StylePosition();
622 :
623 0 : int32_t rowSpan = GetTableFrame()->GetEffectiveRowSpan(*aCellFrame);
624 :
625 0 : const nsStyleCoord& bsizeStyleCoord = position->BSize(aWM);
626 0 : switch (bsizeStyleCoord.GetUnit()) {
627 : case eStyleUnit_Calc: {
628 0 : if (bsizeStyleCoord.CalcHasPercent()) {
629 : // Treat this like "auto"
630 0 : break;
631 : }
632 : // Fall through to the coord case
633 : MOZ_FALLTHROUGH;
634 : }
635 : case eStyleUnit_Coord: {
636 : // In quirks mode, table cell isize should be content-box, but bsize
637 : // should be border-box.
638 : // Because of this historic anomaly, we do not use quirk.css
639 : // (since we can't specify one value of box-sizing for isize and another
640 : // for bsize)
641 0 : specifiedBSize = nsRuleNode::ComputeCoordPercentCalc(bsizeStyleCoord, 0);
642 0 : if (PresContext()->CompatibilityMode() != eCompatibility_NavQuirks &&
643 0 : position->mBoxSizing == StyleBoxSizing::Content) {
644 0 : specifiedBSize +=
645 0 : aCellFrame->GetLogicalUsedBorderAndPadding(aWM).BStartEnd(aWM);
646 : }
647 :
648 0 : if (1 == rowSpan) {
649 0 : SetFixedBSize(specifiedBSize);
650 : }
651 0 : break;
652 : }
653 : case eStyleUnit_Percent: {
654 0 : if (1 == rowSpan) {
655 0 : SetPctBSize(bsizeStyleCoord.GetPercentValue());
656 : }
657 : // pct bsizes are handled when all of the cells are finished,
658 : // so don't set specifiedBSize
659 0 : break;
660 : }
661 : case eStyleUnit_Auto:
662 : default:
663 0 : break;
664 : }
665 :
666 : // If the specified bsize is greater than the desired bsize,
667 : // then use the specified bsize
668 0 : if (specifiedBSize > aDesiredBSize) {
669 0 : aDesiredBSize = specifiedBSize;
670 : }
671 :
672 0 : return NS_OK;
673 : }
674 :
675 : // Calculates the available isize for the table cell based on the known
676 : // column isizes taking into account column spans and column spacing
677 : static nscoord
678 0 : CalcAvailISize(nsTableFrame& aTableFrame,
679 : nsTableCellFrame& aCellFrame)
680 : {
681 0 : nscoord cellAvailISize = 0;
682 : int32_t colIndex;
683 0 : aCellFrame.GetColIndex(colIndex);
684 0 : int32_t colspan = aTableFrame.GetEffectiveColSpan(aCellFrame);
685 0 : NS_ASSERTION(colspan > 0, "effective colspan should be positive");
686 : nsTableFrame* fifTable =
687 0 : static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
688 :
689 0 : for (int32_t spanX = 0; spanX < colspan; spanX++) {
690 0 : cellAvailISize +=
691 0 : fifTable->GetColumnISizeFromFirstInFlow(colIndex + spanX);
692 0 : if (spanX > 0 &&
693 0 : aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) {
694 0 : cellAvailISize += aTableFrame.GetColSpacing(colIndex + spanX - 1);
695 : }
696 : }
697 0 : return cellAvailISize;
698 : }
699 :
700 : nscoord
701 0 : GetSpaceBetween(int32_t aPrevColIndex,
702 : int32_t aColIndex,
703 : int32_t aColSpan,
704 : nsTableFrame& aTableFrame,
705 : bool aCheckVisibility)
706 : {
707 0 : nscoord space = 0;
708 : int32_t colIdx;
709 : nsTableFrame* fifTable =
710 0 : static_cast<nsTableFrame*>(aTableFrame.FirstInFlow());
711 0 : for (colIdx = aPrevColIndex + 1; aColIndex > colIdx; colIdx++) {
712 0 : bool isCollapsed = false;
713 0 : if (!aCheckVisibility) {
714 0 : space += fifTable->GetColumnISizeFromFirstInFlow(colIdx);
715 : }
716 : else {
717 0 : nsTableColFrame* colFrame = aTableFrame.GetColFrame(colIdx);
718 0 : const nsStyleVisibility* colVis = colFrame->StyleVisibility();
719 0 : bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
720 0 : nsIFrame* cgFrame = colFrame->GetParent();
721 0 : const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
722 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
723 0 : groupVis->mVisible);
724 0 : isCollapsed = collapseCol || collapseGroup;
725 0 : if (!isCollapsed)
726 0 : space += fifTable->GetColumnISizeFromFirstInFlow(colIdx);
727 : }
728 0 : if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colIdx)) {
729 0 : space += aTableFrame.GetColSpacing(colIdx - 1);
730 : }
731 : }
732 0 : return space;
733 : }
734 :
735 : // subtract the bsizes of aRow's prev in flows from the unpaginated bsize
736 : static
737 0 : nscoord CalcBSizeFromUnpaginatedBSize(nsTableRowFrame& aRow,
738 : WritingMode aWM)
739 : {
740 0 : nscoord bsize = 0;
741 : nsTableRowFrame* firstInFlow =
742 0 : static_cast<nsTableRowFrame*>(aRow.FirstInFlow());
743 0 : if (firstInFlow->HasUnpaginatedBSize()) {
744 0 : bsize = firstInFlow->GetUnpaginatedBSize();
745 0 : for (nsIFrame* prevInFlow = aRow.GetPrevInFlow(); prevInFlow;
746 : prevInFlow = prevInFlow->GetPrevInFlow()) {
747 0 : bsize -= prevInFlow->BSize(aWM);
748 : }
749 : }
750 0 : return std::max(bsize, 0);
751 : }
752 :
753 : void
754 0 : nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
755 : ReflowOutput& aDesiredSize,
756 : const ReflowInput& aReflowInput,
757 : nsTableFrame& aTableFrame,
758 : nsReflowStatus& aStatus)
759 : {
760 0 : aStatus.Reset();
761 :
762 : // XXXldb Should we be checking constrained bsize instead?
763 0 : const bool isPaginated = aPresContext->IsPaginated();
764 0 : const bool borderCollapse = aTableFrame.IsBorderCollapse();
765 :
766 0 : int32_t cellColSpan = 1; // must be defined here so it's set properly for non-cell kids
767 :
768 : // remember the col index of the previous cell to handle rowspans into this row
769 0 : int32_t prevColIndex = -1;
770 0 : nscoord iCoord = 0; // running total of children inline-coord offset
771 :
772 : // This computes the max of all cell bsizes
773 0 : nscoord cellMaxBSize = 0;
774 :
775 : // Reflow each of our existing cell frames
776 0 : WritingMode wm = aReflowInput.GetWritingMode();
777 : nsSize containerSize =
778 0 : aReflowInput.ComputedSizeAsContainerIfConstrained();
779 :
780 0 : for (nsIFrame* kidFrame : mFrames) {
781 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
782 0 : if (!cellFrame) {
783 : // XXXldb nsCSSFrameConstructor needs to enforce this!
784 0 : NS_NOTREACHED("yikes, a non-row child");
785 :
786 : // it's an unknown frame type, give it a generic reflow and ignore the results
787 : TableCellReflowInput
788 : kidReflowInput(aPresContext, aReflowInput, kidFrame,
789 0 : LogicalSize(kidFrame->GetWritingMode(), 0, 0),
790 0 : ReflowInput::CALLER_WILL_INIT);
791 0 : InitChildReflowInput(*aPresContext, LogicalSize(wm), false, kidReflowInput);
792 0 : ReflowOutput desiredSize(aReflowInput);
793 0 : nsReflowStatus status;
794 0 : ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowInput, 0, 0, 0, status);
795 0 : kidFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
796 :
797 0 : continue;
798 : }
799 :
800 : // See if we should only reflow the dirty child frames
801 0 : bool doReflowChild = true;
802 0 : if (!aReflowInput.ShouldReflowAllKids() &&
803 0 : !aTableFrame.IsGeometryDirty() &&
804 0 : !NS_SUBTREE_DIRTY(kidFrame)) {
805 0 : if (!aReflowInput.mFlags.mSpecialBSizeReflow)
806 0 : doReflowChild = false;
807 : }
808 0 : else if ((NS_UNCONSTRAINEDSIZE != aReflowInput.AvailableBSize())) {
809 : // We don't reflow a rowspan >1 cell here with a constrained bsize.
810 : // That happens in nsTableRowGroupFrame::SplitSpanningCells.
811 0 : if (aTableFrame.GetEffectiveRowSpan(*cellFrame) > 1) {
812 0 : doReflowChild = false;
813 : }
814 : }
815 0 : if (aReflowInput.mFlags.mSpecialBSizeReflow) {
816 0 : if (!isPaginated &&
817 0 : !cellFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
818 0 : continue;
819 : }
820 : }
821 :
822 : int32_t cellColIndex;
823 0 : cellFrame->GetColIndex(cellColIndex);
824 0 : cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame);
825 :
826 : // If the adjacent cell is in a prior row (because of a rowspan) add in the space
827 0 : if (prevColIndex != (cellColIndex - 1)) {
828 0 : iCoord += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame,
829 : false);
830 : }
831 :
832 : // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into
833 0 : prevColIndex = cellColIndex + (cellColSpan - 1);
834 :
835 : // Reflow the child frame
836 0 : nsRect kidRect = kidFrame->GetRect();
837 : LogicalPoint origKidNormalPosition =
838 0 : kidFrame->GetLogicalNormalPosition(wm, containerSize);
839 : // All cells' no-relative-positioning position should be snapped to the
840 : // row's bstart edge.
841 : // This doesn't hold in vertical-rl mode, where we don't yet know the
842 : // correct containerSize for the row frame. In that case, we'll have to
843 : // fix up child positions later, after determining our desiredSize.
844 0 : NS_ASSERTION(origKidNormalPosition.B(wm) == 0 || wm.IsVerticalRL(),
845 : "unexpected kid position");
846 :
847 0 : nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect();
848 0 : LogicalPoint kidPosition(wm, iCoord, 0);
849 0 : bool firstReflow = kidFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW);
850 :
851 0 : if (doReflowChild) {
852 : // Calculate the available isize for the table cell using the known
853 : // column isizes
854 0 : nscoord availCellISize = CalcAvailISize(aTableFrame, *cellFrame);
855 :
856 0 : Maybe<TableCellReflowInput> kidReflowInput;
857 0 : ReflowOutput desiredSize(aReflowInput);
858 :
859 : // If the avail isize is not the same as last time we reflowed the cell or
860 : // the cell wants to be bigger than what was available last time or
861 : // it is a style change reflow or we are printing, then we must reflow the
862 : // cell. Otherwise we can skip the reflow.
863 : // XXXldb Why is this condition distinct from doReflowChild above?
864 0 : WritingMode wm = aReflowInput.GetWritingMode();
865 0 : NS_ASSERTION(cellFrame->GetWritingMode() == wm,
866 : "expected consistent writing-mode within table");
867 0 : LogicalSize cellDesiredSize = cellFrame->GetDesiredSize();
868 0 : if ((availCellISize != cellFrame->GetPriorAvailISize()) ||
869 0 : (cellDesiredSize.ISize(wm) > cellFrame->GetPriorAvailISize()) ||
870 0 : HasAnyStateBits(NS_FRAME_IS_DIRTY) ||
871 0 : isPaginated ||
872 0 : NS_SUBTREE_DIRTY(cellFrame) ||
873 : // See if it needs a special reflow, or if it had one that we need to undo.
874 0 : cellFrame->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE) ||
875 0 : HasPctBSize()) {
876 : // Reflow the cell to fit the available isize, bsize
877 : // XXX The old IR_ChildIsDirty code used availCellISize here.
878 0 : LogicalSize kidAvailSize(wm, availCellISize, aReflowInput.AvailableBSize());
879 :
880 : // Reflow the child
881 0 : kidReflowInput.emplace(aPresContext, aReflowInput, kidFrame,
882 : kidAvailSize,
883 0 : ReflowInput::CALLER_WILL_INIT);
884 0 : InitChildReflowInput(*aPresContext, kidAvailSize, borderCollapse,
885 0 : *kidReflowInput);
886 :
887 0 : nsReflowStatus status;
888 0 : ReflowChild(kidFrame, aPresContext, desiredSize, *kidReflowInput,
889 0 : wm, kidPosition, containerSize, 0, status);
890 :
891 : // allow the table to determine if/how the table needs to be rebalanced
892 : // If any of the cells are not complete, then we're not complete
893 0 : if (status.IsIncomplete()) {
894 0 : aStatus.Reset();
895 0 : aStatus.SetIncomplete();
896 : }
897 : } else {
898 0 : if (iCoord != origKidNormalPosition.I(wm)) {
899 0 : kidFrame->InvalidateFrameSubtree();
900 : }
901 :
902 0 : desiredSize.SetSize(wm, cellDesiredSize);
903 0 : desiredSize.mOverflowAreas = cellFrame->GetOverflowAreas();
904 :
905 : // if we are in a floated table, our position is not yet established, so we cannot reposition our views
906 : // the containing block will do this for us after positioning the table
907 0 : if (!aTableFrame.IsFloating()) {
908 : // Because we may have moved the frame we need to make sure any views are
909 : // positioned properly. We have to do this, because any one of our parent
910 : // frames could have moved and we have no way of knowing...
911 0 : nsTableFrame::RePositionViews(kidFrame);
912 : }
913 : }
914 :
915 0 : if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
916 0 : if (!GetPrevInFlow()) {
917 : // Calculate the cell's actual bsize given its pass2 bsize. This
918 : // function takes into account the specified bsize (in the style)
919 0 : CalculateCellActualBSize(cellFrame, desiredSize.BSize(wm), wm);
920 : }
921 : // bsize may have changed, adjust descent to absorb any excess difference
922 : nscoord ascent;
923 0 : if (!kidFrame->PrincipalChildList().FirstChild()->PrincipalChildList().FirstChild()) {
924 0 : ascent = desiredSize.BSize(wm);
925 : } else {
926 0 : ascent = ((nsTableCellFrame *)kidFrame)->GetCellBaseline();
927 : }
928 0 : nscoord descent = desiredSize.BSize(wm) - ascent;
929 0 : UpdateBSize(desiredSize.BSize(wm), ascent, descent, &aTableFrame, cellFrame);
930 : } else {
931 0 : cellMaxBSize = std::max(cellMaxBSize, desiredSize.BSize(wm));
932 0 : int32_t rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame);
933 0 : if (1 == rowSpan) {
934 0 : SetContentBSize(cellMaxBSize);
935 : }
936 : }
937 :
938 : // Place the child
939 0 : desiredSize.ISize(wm) = availCellISize;
940 :
941 0 : if (kidReflowInput) {
942 : // We reflowed. Apply relative positioning in the normal way.
943 0 : kidReflowInput->ApplyRelativePositioning(&kidPosition, containerSize);
944 0 : } else if (kidFrame->IsRelativelyPositioned()) {
945 : // We didn't reflow. Do the positioning part of what
946 : // MovePositionBy does internally. (This codepath should really
947 : // be merged into the else below if we can.)
948 : nsMargin* computedOffsetProp =
949 0 : kidFrame->GetProperty(nsIFrame::ComputedOffsetProperty());
950 : // Bug 975644: a position:sticky kid can end up with a null
951 : // property value here.
952 0 : LogicalMargin computedOffsets(wm, computedOffsetProp ?
953 0 : *computedOffsetProp : nsMargin());
954 : ReflowInput::ApplyRelativePositioning(kidFrame, wm, computedOffsets,
955 0 : &kidPosition, containerSize);
956 : }
957 :
958 : // In vertical-rl mode, we are likely to have containerSize.width = 0
959 : // because ComputedWidth() was NS_UNCONSTRAINEDSIZE.
960 : // For cases where that's wrong, we will fix up the position later.
961 : FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr,
962 0 : wm, kidPosition, containerSize, 0);
963 :
964 0 : nsTableFrame* tableFrame = GetTableFrame();
965 0 : if (tableFrame->IsBorderCollapse()) {
966 0 : nsTableFrame::InvalidateTableFrame(kidFrame, kidRect, kidVisualOverflow,
967 0 : firstReflow);
968 : }
969 :
970 0 : iCoord += desiredSize.ISize(wm);
971 : } else {
972 0 : if (iCoord != origKidNormalPosition.I(wm)) {
973 : // Invalidate the old position
974 0 : kidFrame->InvalidateFrameSubtree();
975 : // Move to the new position. As above, we need to account for relative
976 : // positioning.
977 0 : kidFrame->MovePositionBy(wm,
978 0 : LogicalPoint(wm, iCoord - origKidNormalPosition.I(wm), 0));
979 0 : nsTableFrame::RePositionViews(kidFrame);
980 : // invalidate the new position
981 0 : kidFrame->InvalidateFrameSubtree();
982 : }
983 : // we need to account for the cell's isize even if it isn't reflowed
984 0 : iCoord += kidFrame->ISize(wm);
985 :
986 0 : if (kidFrame->GetNextInFlow()) {
987 0 : aStatus.Reset();
988 0 : aStatus.SetIncomplete();
989 : }
990 : }
991 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
992 0 : iCoord += aTableFrame.GetColSpacing(cellColIndex);
993 : }
994 :
995 : // Just set our isize to what was available.
996 : // The table will calculate the isize and not use our value.
997 0 : aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
998 :
999 0 : if (aReflowInput.mFlags.mSpecialBSizeReflow) {
1000 0 : aDesiredSize.BSize(wm) = BSize(wm);
1001 0 : } else if (NS_UNCONSTRAINEDSIZE == aReflowInput.AvailableBSize()) {
1002 0 : aDesiredSize.BSize(wm) = CalcBSize(aReflowInput);
1003 0 : if (GetPrevInFlow()) {
1004 0 : nscoord bsize = CalcBSizeFromUnpaginatedBSize(*this, wm);
1005 0 : aDesiredSize.BSize(wm) = std::max(aDesiredSize.BSize(wm), bsize);
1006 : } else {
1007 0 : if (isPaginated && HasStyleBSize()) {
1008 : // set the unpaginated bsize so next in flows can try to honor it
1009 0 : SetHasUnpaginatedBSize(true);
1010 0 : SetUnpaginatedBSize(aPresContext, aDesiredSize.BSize(wm));
1011 : }
1012 0 : if (isPaginated && HasUnpaginatedBSize()) {
1013 0 : aDesiredSize.BSize(wm) = std::max(aDesiredSize.BSize(wm),
1014 0 : GetUnpaginatedBSize());
1015 : }
1016 : }
1017 : } else { // constrained bsize, paginated
1018 : // Compute the bsize we should have from style (subtracting the
1019 : // bsize from our prev-in-flows from the style bsize)
1020 0 : nscoord styleBSize = CalcBSizeFromUnpaginatedBSize(*this, wm);
1021 0 : if (styleBSize > aReflowInput.AvailableBSize()) {
1022 0 : styleBSize = aReflowInput.AvailableBSize();
1023 0 : aStatus.SetIncomplete();
1024 : }
1025 0 : aDesiredSize.BSize(wm) = std::max(cellMaxBSize, styleBSize);
1026 : }
1027 :
1028 0 : if (wm.IsVerticalRL()) {
1029 : // Any children whose width was not the same as our final
1030 : // aDesiredSize.BSize will have been misplaced earlier at the
1031 : // FinishReflowChild stage. So fix them up now.
1032 0 : for (nsIFrame* kidFrame : mFrames) {
1033 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1034 0 : if (!cellFrame) {
1035 0 : continue;
1036 : }
1037 0 : if (kidFrame->BSize(wm) != aDesiredSize.BSize(wm)) {
1038 : kidFrame->MovePositionBy(wm,
1039 0 : LogicalPoint(wm, 0, kidFrame->BSize(wm) - aDesiredSize.BSize(wm)));
1040 0 : nsTableFrame::RePositionViews(kidFrame);
1041 : // Do we need to InvalidateFrameSubtree() here?
1042 : }
1043 : }
1044 : }
1045 :
1046 0 : aDesiredSize.UnionOverflowAreasWithDesiredBounds();
1047 0 : FinishAndStoreOverflow(&aDesiredSize);
1048 0 : }
1049 :
1050 : /** Layout the entire row.
1051 : * This method stacks cells in the inline dir according to HTML 4.0 rules.
1052 : */
1053 : void
1054 0 : nsTableRowFrame::Reflow(nsPresContext* aPresContext,
1055 : ReflowOutput& aDesiredSize,
1056 : const ReflowInput& aReflowInput,
1057 : nsReflowStatus& aStatus)
1058 : {
1059 0 : MarkInReflow();
1060 0 : DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
1061 0 : DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
1062 :
1063 0 : WritingMode wm = aReflowInput.GetWritingMode();
1064 :
1065 0 : nsTableFrame* tableFrame = GetTableFrame();
1066 0 : const nsStyleVisibility* rowVis = StyleVisibility();
1067 0 : bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1068 0 : if (collapseRow) {
1069 0 : tableFrame->SetNeedToCollapse(true);
1070 : }
1071 :
1072 : // see if a special bsize reflow needs to occur due to having a pct bsize
1073 0 : nsTableFrame::CheckRequestSpecialBSizeReflow(aReflowInput);
1074 :
1075 : // See if we have a cell with specified/pct bsize
1076 0 : InitHasCellWithStyleBSize(tableFrame);
1077 :
1078 0 : ReflowChildren(aPresContext, aDesiredSize, aReflowInput, *tableFrame, aStatus);
1079 :
1080 0 : if (aPresContext->IsPaginated() && !aStatus.IsFullyComplete() &&
1081 0 : ShouldAvoidBreakInside(aReflowInput)) {
1082 0 : aStatus.SetInlineLineBreakBeforeAndReset();
1083 : }
1084 :
1085 : // Just set our isize to what was available.
1086 : // The table will calculate the isize and not use our value.
1087 0 : aDesiredSize.ISize(wm) = aReflowInput.AvailableISize();
1088 :
1089 : // If our parent is in initial reflow, it'll handle invalidating our
1090 : // entire overflow rect.
1091 0 : if (!GetParent()->HasAnyStateBits(NS_FRAME_FIRST_REFLOW) &&
1092 0 : nsSize(aDesiredSize.Width(), aDesiredSize.Height()) != mRect.Size()) {
1093 0 : InvalidateFrame();
1094 : }
1095 :
1096 : // Any absolutely-positioned children will get reflowed in
1097 : // nsFrame::FixupPositionedTableParts in another pass, so propagate our
1098 : // dirtiness to them before our parent clears our dirty bits.
1099 0 : PushDirtyBitToAbsoluteFrames();
1100 :
1101 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
1102 0 : }
1103 :
1104 : /**
1105 : * This function is called by the row group frame's SplitRowGroup() code when
1106 : * pushing a row frame that has cell frames that span into it. The cell frame
1107 : * should be reflowed with the specified height
1108 : */
1109 : nscoord
1110 0 : nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext,
1111 : const ReflowInput& aReflowInput,
1112 : bool aIsTopOfPage,
1113 : nsTableCellFrame* aCellFrame,
1114 : nscoord aAvailableBSize,
1115 : nsReflowStatus& aStatus)
1116 : {
1117 0 : WritingMode wm = aReflowInput.GetWritingMode();
1118 :
1119 : // Reflow the cell frame with the specified height. Use the existing width
1120 0 : nsSize containerSize = aCellFrame->GetSize();
1121 0 : LogicalRect cellRect = aCellFrame->GetLogicalRect(wm, containerSize);
1122 0 : nsRect cellVisualOverflow = aCellFrame->GetVisualOverflowRect();
1123 :
1124 0 : LogicalSize cellSize = cellRect.Size(wm);
1125 0 : LogicalSize availSize(wm, cellRect.ISize(wm), aAvailableBSize);
1126 0 : bool borderCollapse = GetTableFrame()->IsBorderCollapse();
1127 0 : NS_ASSERTION(aCellFrame->GetWritingMode() == wm,
1128 : "expected consistent writing-mode within table");
1129 : TableCellReflowInput
1130 : cellReflowInput(aPresContext, aReflowInput, aCellFrame, availSize,
1131 0 : ReflowInput::CALLER_WILL_INIT);
1132 0 : InitChildReflowInput(*aPresContext, availSize, borderCollapse, cellReflowInput);
1133 0 : cellReflowInput.mFlags.mIsTopOfPage = aIsTopOfPage;
1134 :
1135 0 : ReflowOutput desiredSize(aReflowInput);
1136 :
1137 0 : ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowInput,
1138 0 : 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
1139 0 : bool fullyComplete = aStatus.IsComplete() && !aStatus.IsTruncated();
1140 0 : if (fullyComplete) {
1141 0 : desiredSize.BSize(wm) = aAvailableBSize;
1142 : }
1143 0 : aCellFrame->SetSize(wm, LogicalSize(wm, cellSize.ISize(wm),
1144 0 : desiredSize.BSize(wm)));
1145 :
1146 : // Note: BlockDirAlignChild can affect the overflow rect.
1147 : // XXX What happens if this cell has 'vertical-align: baseline' ?
1148 : // XXX Why is it assumed that the cell's ascent hasn't changed ?
1149 0 : if (fullyComplete) {
1150 0 : aCellFrame->BlockDirAlignChild(wm, mMaxCellAscent);
1151 : }
1152 :
1153 0 : nsTableFrame::InvalidateTableFrame(aCellFrame,
1154 0 : cellRect.GetPhysicalRect(wm, containerSize),
1155 : cellVisualOverflow,
1156 : aCellFrame->
1157 0 : HasAnyStateBits(NS_FRAME_FIRST_REFLOW));
1158 :
1159 0 : aCellFrame->DidReflow(aPresContext, nullptr, nsDidReflowStatus::FINISHED);
1160 :
1161 0 : return desiredSize.BSize(wm);
1162 : }
1163 :
1164 : nscoord
1165 0 : nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset,
1166 : nscoord aISize,
1167 : bool aCollapseGroup,
1168 : bool& aDidCollapse)
1169 : {
1170 0 : const nsStyleVisibility* rowVis = StyleVisibility();
1171 0 : bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1172 : nsTableFrame* tableFrame =
1173 0 : static_cast<nsTableFrame*>(GetTableFrame()->FirstInFlow());
1174 0 : if (collapseRow) {
1175 0 : tableFrame->SetNeedToCollapse(true);
1176 : }
1177 :
1178 0 : if (aRowOffset != 0) {
1179 : // We're moving, so invalidate our old position
1180 0 : InvalidateFrameSubtree();
1181 : }
1182 :
1183 0 : WritingMode wm = GetWritingMode();
1184 :
1185 0 : nsSize parentSize = GetParent()->GetSize();
1186 0 : LogicalRect rowRect = GetLogicalRect(wm, parentSize);
1187 0 : nsRect oldRect = mRect;
1188 0 : nsRect oldVisualOverflow = GetVisualOverflowRect();
1189 :
1190 0 : rowRect.BStart(wm) -= aRowOffset;
1191 0 : rowRect.ISize(wm) = aISize;
1192 0 : nsOverflowAreas overflow;
1193 0 : nscoord shift = 0;
1194 0 : nsSize containerSize = mRect.Size();
1195 :
1196 0 : if (aCollapseGroup || collapseRow) {
1197 0 : aDidCollapse = true;
1198 0 : shift = rowRect.BSize(wm);
1199 0 : nsTableCellFrame* cellFrame = GetFirstCell();
1200 0 : if (cellFrame) {
1201 : int32_t rowIndex;
1202 0 : cellFrame->GetRowIndex(rowIndex);
1203 0 : shift += tableFrame->GetRowSpacing(rowIndex);
1204 0 : while (cellFrame) {
1205 0 : LogicalRect cRect = cellFrame->GetLogicalRect(wm, containerSize);
1206 : // If aRowOffset != 0, there's no point in invalidating the cells, since
1207 : // we've already invalidated our overflow area. Note that we _do_ still
1208 : // need to invalidate if our row is not moving, because the cell might
1209 : // span out of this row, so invalidating our row rect won't do enough.
1210 0 : if (aRowOffset == 0) {
1211 0 : InvalidateFrame();
1212 : }
1213 0 : cRect.BSize(wm) = 0;
1214 0 : cellFrame->SetRect(wm, cRect, containerSize);
1215 0 : cellFrame = cellFrame->GetNextCell();
1216 : }
1217 : } else {
1218 0 : shift += tableFrame->GetRowSpacing(GetRowIndex());
1219 : }
1220 0 : rowRect.BSize(wm) = 0;
1221 : }
1222 : else { // row is not collapsed
1223 : // remember the col index of the previous cell to handle rowspans into this
1224 : // row
1225 0 : int32_t prevColIndex = -1;
1226 0 : nscoord iPos = 0; // running total of children inline-axis offset
1227 : nsTableFrame* fifTable =
1228 0 : static_cast<nsTableFrame*>(tableFrame->FirstInFlow());
1229 :
1230 0 : for (nsIFrame* kidFrame : mFrames) {
1231 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1232 0 : if (cellFrame) {
1233 : int32_t cellColIndex;
1234 0 : cellFrame->GetColIndex(cellColIndex);
1235 0 : int32_t cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame);
1236 :
1237 : // If the adjacent cell is in a prior row (because of a rowspan) add in
1238 : // the space
1239 0 : if (prevColIndex != (cellColIndex - 1)) {
1240 0 : iPos += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
1241 : *tableFrame, true);
1242 : }
1243 0 : LogicalRect cRect(wm, iPos, 0, 0, rowRect.BSize(wm));
1244 :
1245 : // remember the last (iend-wards-most) column this cell spans into
1246 0 : prevColIndex = cellColIndex + cellColSpan - 1;
1247 0 : int32_t actualColSpan = cellColSpan;
1248 0 : bool isVisible = false;
1249 0 : for (int32_t colIdx = cellColIndex; actualColSpan > 0;
1250 : colIdx++, actualColSpan--) {
1251 :
1252 0 : nsTableColFrame* colFrame = tableFrame->GetColFrame(colIdx);
1253 0 : const nsStyleVisibility* colVis = colFrame->StyleVisibility();
1254 : bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE ==
1255 0 : colVis->mVisible);
1256 0 : nsIFrame* cgFrame = colFrame->GetParent();
1257 0 : const nsStyleVisibility* groupVis = cgFrame->StyleVisibility();
1258 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
1259 0 : groupVis->mVisible);
1260 0 : bool isCollapsed = collapseCol || collapseGroup;
1261 0 : if (!isCollapsed) {
1262 0 : cRect.ISize(wm) += fifTable->GetColumnISizeFromFirstInFlow(colIdx);
1263 0 : isVisible = true;
1264 0 : if ((actualColSpan > 1)) {
1265 : nsTableColFrame* nextColFrame =
1266 0 : tableFrame->GetColFrame(colIdx + 1);
1267 : const nsStyleVisibility* nextColVis =
1268 0 : nextColFrame->StyleVisibility();
1269 0 : if ( (NS_STYLE_VISIBILITY_COLLAPSE != nextColVis->mVisible) &&
1270 0 : tableFrame->ColumnHasCellSpacingBefore(colIdx + 1)) {
1271 0 : cRect.ISize(wm) += tableFrame->GetColSpacing(cellColIndex);
1272 : }
1273 : }
1274 : }
1275 : }
1276 0 : iPos += cRect.ISize(wm);
1277 0 : if (isVisible) {
1278 0 : iPos += tableFrame->GetColSpacing(cellColIndex);
1279 : }
1280 0 : int32_t actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame);
1281 0 : nsTableRowFrame* rowFrame = GetNextRow();
1282 0 : for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) {
1283 0 : const nsStyleVisibility* nextRowVis = rowFrame->StyleVisibility();
1284 : bool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE ==
1285 0 : nextRowVis->mVisible);
1286 0 : if (!collapseNextRow) {
1287 : LogicalRect nextRect = rowFrame->GetLogicalRect(wm,
1288 0 : containerSize);
1289 0 : cRect.BSize(wm) +=
1290 0 : nextRect.BSize(wm) +
1291 0 : tableFrame->GetRowSpacing(rowFrame->GetRowIndex());
1292 : }
1293 0 : rowFrame = rowFrame->GetNextRow();
1294 : }
1295 :
1296 0 : nsRect oldCellRect = cellFrame->GetRect();
1297 : LogicalPoint oldCellNormalPos =
1298 0 : cellFrame->GetLogicalNormalPosition(wm, containerSize);
1299 :
1300 0 : nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect();
1301 :
1302 0 : if (aRowOffset == 0 && cRect.Origin(wm) != oldCellNormalPos) {
1303 : // We're moving the cell. Invalidate the old overflow area
1304 0 : cellFrame->InvalidateFrameSubtree();
1305 : }
1306 :
1307 0 : cellFrame->MovePositionBy(wm, cRect.Origin(wm) - oldCellNormalPos);
1308 0 : cellFrame->SetSize(wm, cRect.Size(wm));
1309 :
1310 : // XXXbz This looks completely bogus in the cases when we didn't
1311 : // collapse the cell!
1312 0 : LogicalRect cellBounds(wm, 0, 0, cRect.ISize(wm), cRect.BSize(wm));
1313 : nsRect cellPhysicalBounds =
1314 0 : cellBounds.GetPhysicalRect(wm, containerSize);
1315 0 : nsOverflowAreas cellOverflow(cellPhysicalBounds, cellPhysicalBounds);
1316 0 : cellFrame->FinishAndStoreOverflow(cellOverflow,
1317 0 : cRect.Size(wm).GetPhysicalSize(wm));
1318 0 : nsTableFrame::RePositionViews(cellFrame);
1319 0 : ConsiderChildOverflow(overflow, cellFrame);
1320 :
1321 0 : if (aRowOffset == 0) {
1322 : nsTableFrame::InvalidateTableFrame(cellFrame, oldCellRect,
1323 0 : oldCellVisualOverflow, false);
1324 : }
1325 : }
1326 : }
1327 : }
1328 :
1329 0 : SetRect(wm, rowRect, containerSize);
1330 0 : overflow.UnionAllWith(nsRect(0, 0, rowRect.Width(wm), rowRect.Height(wm)));
1331 0 : FinishAndStoreOverflow(overflow, rowRect.Size(wm).GetPhysicalSize(wm));
1332 :
1333 0 : nsTableFrame::RePositionViews(this);
1334 0 : nsTableFrame::InvalidateTableFrame(this, oldRect, oldVisualOverflow, false);
1335 0 : return shift;
1336 : }
1337 :
1338 : /*
1339 : * The following method is called by the row group frame's SplitRowGroup()
1340 : * when it creates a continuing cell frame and wants to insert it into the
1341 : * row's child list.
1342 : */
1343 : void
1344 0 : nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame,
1345 : int32_t aColIndex)
1346 : {
1347 : // Find the cell frame where col index < aColIndex
1348 0 : nsTableCellFrame* priorCell = nullptr;
1349 0 : for (nsIFrame* child : mFrames) {
1350 0 : nsTableCellFrame *cellFrame = do_QueryFrame(child);
1351 0 : if (cellFrame) {
1352 : int32_t colIndex;
1353 0 : cellFrame->GetColIndex(colIndex);
1354 0 : if (colIndex < aColIndex) {
1355 0 : priorCell = cellFrame;
1356 : }
1357 0 : else break;
1358 : }
1359 : }
1360 0 : mFrames.InsertFrame(this, priorCell, aFrame);
1361 0 : }
1362 :
1363 : nsTableRowFrame*
1364 0 : nsTableRowFrame::GetNextRow() const
1365 : {
1366 0 : nsIFrame* childFrame = GetNextSibling();
1367 0 : while (childFrame) {
1368 0 : nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
1369 0 : if (rowFrame) {
1370 0 : NS_ASSERTION(mozilla::StyleDisplay::TableRow == childFrame->StyleDisplay()->mDisplay,
1371 : "wrong display type on rowframe");
1372 0 : return rowFrame;
1373 : }
1374 0 : childFrame = childFrame->GetNextSibling();
1375 : }
1376 0 : return nullptr;
1377 : }
1378 :
1379 0 : NS_DECLARE_FRAME_PROPERTY_SMALL_VALUE(RowUnpaginatedHeightProperty, nscoord)
1380 :
1381 : void
1382 0 : nsTableRowFrame::SetUnpaginatedBSize(nsPresContext* aPresContext,
1383 : nscoord aValue)
1384 : {
1385 0 : NS_ASSERTION(!GetPrevInFlow(), "program error");
1386 : // Set the property
1387 0 : SetProperty(RowUnpaginatedHeightProperty(), aValue);
1388 0 : }
1389 :
1390 : nscoord
1391 0 : nsTableRowFrame::GetUnpaginatedBSize()
1392 : {
1393 0 : return GetProperty(RowUnpaginatedHeightProperty());
1394 : }
1395 :
1396 0 : void nsTableRowFrame::SetContinuousBCBorderWidth(LogicalSide aForSide,
1397 : BCPixelSize aPixelValue)
1398 : {
1399 0 : switch (aForSide) {
1400 : case eLogicalSideIEnd:
1401 0 : mIEndContBorderWidth = aPixelValue;
1402 0 : return;
1403 : case eLogicalSideBStart:
1404 0 : mBStartContBorderWidth = aPixelValue;
1405 0 : return;
1406 : case eLogicalSideIStart:
1407 0 : mIStartContBorderWidth = aPixelValue;
1408 0 : return;
1409 : default:
1410 0 : NS_ERROR("invalid LogicalSide arg");
1411 : }
1412 : }
1413 : #ifdef ACCESSIBILITY
1414 : a11y::AccType
1415 0 : nsTableRowFrame::AccessibleType()
1416 : {
1417 0 : return a11y::eHTMLTableRowType;
1418 : }
1419 : #endif
1420 : /**
1421 : * Sets the NS_ROW_HAS_CELL_WITH_STYLE_BSIZE bit to indicate whether
1422 : * this row has any cells that have non-auto-bsize. (Row-spanning
1423 : * cells are ignored.)
1424 : */
1425 0 : void nsTableRowFrame::InitHasCellWithStyleBSize(nsTableFrame* aTableFrame)
1426 : {
1427 0 : WritingMode wm = GetWritingMode();
1428 :
1429 0 : for (nsIFrame* kidFrame : mFrames) {
1430 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1431 0 : if (!cellFrame) {
1432 0 : NS_NOTREACHED("Table row has a non-cell child.");
1433 0 : continue;
1434 : }
1435 : // Ignore row-spanning cells
1436 0 : const nsStyleCoord &cellBSize = cellFrame->StylePosition()->BSize(wm);
1437 0 : if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
1438 0 : cellBSize.GetUnit() != eStyleUnit_Auto &&
1439 : /* calc() with percentages treated like 'auto' */
1440 0 : (!cellBSize.IsCalcUnit() || !cellBSize.HasPercent())) {
1441 0 : AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE);
1442 0 : return;
1443 : }
1444 : }
1445 0 : RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_BSIZE);
1446 : }
1447 :
1448 : void
1449 0 : nsTableRowFrame::InvalidateFrame(uint32_t aDisplayItemKey)
1450 : {
1451 0 : nsIFrame::InvalidateFrame(aDisplayItemKey);
1452 0 : GetParent()->InvalidateFrameWithRect(GetVisualOverflowRect() + GetPosition(), aDisplayItemKey);
1453 0 : }
1454 :
1455 : void
1456 0 : nsTableRowFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey)
1457 : {
1458 0 : nsIFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
1459 : // If we have filters applied that would affects our bounds, then
1460 : // we get an inactive layer created and this is computed
1461 : // within FrameLayerBuilder
1462 0 : GetParent()->InvalidateFrameWithRect(aRect + GetPosition(), aDisplayItemKey);
1463 0 : }
1464 :
1465 : /* ----- global methods ----- */
1466 :
1467 : nsTableRowFrame*
1468 0 : NS_NewTableRowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1469 : {
1470 0 : return new (aPresShell) nsTableRowFrame(aContext);
1471 : }
1472 :
1473 0 : NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame)
1474 :
1475 : #ifdef DEBUG_FRAME_DUMP
1476 : nsresult
1477 0 : nsTableRowFrame::GetFrameName(nsAString& aResult) const
1478 : {
1479 0 : return MakeFrameName(NS_LITERAL_STRING("TableRow"), aResult);
1480 : }
1481 : #endif
|