1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 : #include "nsTableRowFrame.h"
38 : #include "nsTableRowGroupFrame.h"
39 : #include "nsIPresShell.h"
40 : #include "nsPresContext.h"
41 : #include "nsStyleContext.h"
42 : #include "nsStyleConsts.h"
43 : #include "nsGkAtoms.h"
44 : #include "nsIContent.h"
45 : #include "nsTableFrame.h"
46 : #include "nsTableCellFrame.h"
47 : #include "nsCSSRendering.h"
48 : #include "nsHTMLParts.h"
49 : #include "nsTableColGroupFrame.h"
50 : #include "nsTableColFrame.h"
51 : #include "nsCOMPtr.h"
52 : #include "nsDisplayList.h"
53 :
54 : using namespace mozilla;
55 :
56 : struct nsTableCellReflowState : public nsHTMLReflowState
57 0 : {
58 0 : nsTableCellReflowState(nsPresContext* aPresContext,
59 : const nsHTMLReflowState& aParentReflowState,
60 : nsIFrame* aFrame,
61 : const nsSize& aAvailableSpace,
62 : bool aInit = true)
63 : : nsHTMLReflowState(aPresContext, aParentReflowState, aFrame,
64 0 : aAvailableSpace, -1, -1, aInit)
65 : {
66 0 : }
67 :
68 : void FixUp(const nsSize& aAvailSpace);
69 : };
70 :
71 0 : void nsTableCellReflowState::FixUp(const nsSize& aAvailSpace)
72 : {
73 : // fix the mComputed values during a pass 2 reflow since the cell can be a percentage base
74 0 : NS_WARN_IF_FALSE(NS_UNCONSTRAINEDSIZE != aAvailSpace.width,
75 : "have unconstrained width; this should only result from "
76 : "very large sizes, not attempts at intrinsic width "
77 : "calculation");
78 0 : if (NS_UNCONSTRAINEDSIZE != ComputedWidth()) {
79 : nscoord computedWidth =
80 0 : aAvailSpace.width - mComputedBorderPadding.LeftRight();
81 0 : computedWidth = NS_MAX(0, computedWidth);
82 0 : SetComputedWidth(computedWidth);
83 : }
84 0 : if (NS_UNCONSTRAINEDSIZE != ComputedHeight() &&
85 : NS_UNCONSTRAINEDSIZE != aAvailSpace.height) {
86 : nscoord computedHeight =
87 0 : aAvailSpace.height - mComputedBorderPadding.TopBottom();
88 0 : computedHeight = NS_MAX(0, computedHeight);
89 0 : SetComputedHeight(computedHeight);
90 : }
91 0 : }
92 :
93 : void
94 0 : nsTableRowFrame::InitChildReflowState(nsPresContext& aPresContext,
95 : const nsSize& aAvailSize,
96 : bool aBorderCollapse,
97 : nsTableCellReflowState& aReflowState)
98 : {
99 0 : nsMargin collapseBorder;
100 0 : nsMargin* pCollapseBorder = nsnull;
101 0 : if (aBorderCollapse) {
102 : // we only reflow cells, so don't need to check frame type
103 0 : nsBCTableCellFrame* bcCellFrame = (nsBCTableCellFrame*)aReflowState.frame;
104 0 : if (bcCellFrame) {
105 0 : pCollapseBorder = bcCellFrame->GetBorderWidth(collapseBorder);
106 : }
107 : }
108 0 : aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder);
109 0 : aReflowState.FixUp(aAvailSize);
110 0 : }
111 :
112 : void
113 0 : nsTableRowFrame::SetFixedHeight(nscoord aValue)
114 : {
115 0 : nscoord height = NS_MAX(0, aValue);
116 0 : if (HasFixedHeight()) {
117 0 : if (height > mStyleFixedHeight) {
118 0 : mStyleFixedHeight = height;
119 : }
120 : }
121 : else {
122 0 : mStyleFixedHeight = height;
123 0 : if (height > 0) {
124 0 : SetHasFixedHeight(true);
125 : }
126 : }
127 0 : }
128 :
129 : void
130 0 : nsTableRowFrame::SetPctHeight(float aPctValue,
131 : bool aForce)
132 : {
133 0 : nscoord height = NS_MAX(0, NSToCoordRound(aPctValue * 100.0f));
134 0 : if (HasPctHeight()) {
135 0 : if ((height > mStylePctHeight) || aForce) {
136 0 : mStylePctHeight = height;
137 : }
138 : }
139 : else {
140 0 : mStylePctHeight = height;
141 0 : if (height > 0) {
142 0 : SetHasPctHeight(true);
143 : }
144 : }
145 0 : }
146 :
147 : /* ----------- nsTableRowFrame ---------- */
148 :
149 0 : NS_QUERYFRAME_HEAD(nsTableRowFrame)
150 0 : NS_QUERYFRAME_ENTRY(nsTableRowFrame)
151 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
152 :
153 0 : nsTableRowFrame::nsTableRowFrame(nsStyleContext* aContext)
154 0 : : nsContainerFrame(aContext)
155 : {
156 0 : mBits.mRowIndex = mBits.mFirstInserted = 0;
157 0 : ResetHeight(0);
158 0 : }
159 :
160 0 : nsTableRowFrame::~nsTableRowFrame()
161 : {
162 0 : }
163 :
164 : NS_IMETHODIMP
165 0 : nsTableRowFrame::Init(nsIContent* aContent,
166 : nsIFrame* aParent,
167 : nsIFrame* aPrevInFlow)
168 : {
169 : nsresult rv;
170 :
171 : // Let the base class do its initialization
172 0 : rv = nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
173 :
174 0 : NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == GetStyleDisplay()->mDisplay,
175 : "wrong display on table row frame");
176 :
177 0 : if (aPrevInFlow) {
178 : // Set the row index
179 0 : nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
180 :
181 0 : SetRowIndex(rowFrame->GetRowIndex());
182 : }
183 :
184 0 : return rv;
185 : }
186 :
187 : /* virtual */ void
188 0 : nsTableRowFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
189 : {
190 0 : if (!aOldStyleContext) //avoid this on init
191 0 : return;
192 :
193 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
194 0 : if (tableFrame->IsBorderCollapse() &&
195 0 : tableFrame->BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
196 0 : nsIntRect damageArea(0, GetRowIndex(), tableFrame->GetColCount(), 1);
197 0 : tableFrame->AddBCDamageArea(damageArea);
198 : }
199 : }
200 :
201 : NS_IMETHODIMP
202 0 : nsTableRowFrame::AppendFrames(ChildListID aListID,
203 : nsFrameList& aFrameList)
204 : {
205 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
206 :
207 0 : const nsFrameList::Slice& newCells = mFrames.AppendFrames(nsnull, aFrameList);
208 :
209 : // Add the new cell frames to the table
210 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
211 0 : for (nsFrameList::Enumerator e(newCells) ; !e.AtEnd(); e.Next()) {
212 0 : nsIFrame *childFrame = e.get();
213 0 : NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure");
214 0 : tableFrame->AppendCell(static_cast<nsTableCellFrame&>(*childFrame), GetRowIndex());
215 : }
216 :
217 0 : PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
218 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
219 0 : tableFrame->SetGeometryDirty();
220 :
221 0 : return NS_OK;
222 : }
223 :
224 :
225 : NS_IMETHODIMP
226 0 : nsTableRowFrame::InsertFrames(ChildListID aListID,
227 : nsIFrame* aPrevFrame,
228 : nsFrameList& aFrameList)
229 : {
230 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
231 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
232 : "inserting after sibling frame with different parent");
233 : //Insert Frames in the frame list
234 0 : const nsFrameList::Slice& newCells = mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
235 :
236 : // Get the table frame
237 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
238 0 : nsIAtom* cellFrameType = tableFrame->IsBorderCollapse() ? nsGkAtoms::bcTableCellFrame : nsGkAtoms::tableCellFrame;
239 0 : nsTableCellFrame* prevCellFrame = (nsTableCellFrame *)nsTableFrame::GetFrameAtOrBefore(this, aPrevFrame, cellFrameType);
240 0 : nsTArray<nsTableCellFrame*> cellChildren;
241 0 : for (nsFrameList::Enumerator e(newCells); !e.AtEnd(); e.Next()) {
242 0 : nsIFrame *childFrame = e.get();
243 0 : NS_ASSERTION(IS_TABLE_CELL(childFrame->GetType()),"Not a table cell frame/pseudo frame construction failure");
244 0 : cellChildren.AppendElement(static_cast<nsTableCellFrame*>(childFrame));
245 : }
246 : // insert the cells into the cell map
247 0 : PRInt32 colIndex = -1;
248 0 : if (prevCellFrame) {
249 0 : prevCellFrame->GetColIndex(colIndex);
250 : }
251 0 : tableFrame->InsertCells(cellChildren, GetRowIndex(), colIndex);
252 :
253 0 : PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
254 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
255 0 : tableFrame->SetGeometryDirty();
256 :
257 0 : return NS_OK;
258 : }
259 :
260 : NS_IMETHODIMP
261 0 : nsTableRowFrame::RemoveFrame(ChildListID aListID,
262 : nsIFrame* aOldFrame)
263 : {
264 0 : NS_ASSERTION(aListID == kPrincipalList, "unexpected child list");
265 :
266 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
267 0 : nsTableCellFrame *cellFrame = do_QueryFrame(aOldFrame);
268 0 : if (cellFrame) {
269 : PRInt32 colIndex;
270 0 : cellFrame->GetColIndex(colIndex);
271 : // remove the cell from the cell map
272 0 : tableFrame->RemoveCell(cellFrame, GetRowIndex());
273 :
274 : // Remove the frame and destroy it
275 0 : mFrames.DestroyFrame(aOldFrame);
276 :
277 0 : PresContext()->PresShell()->
278 : FrameNeedsReflow(this, nsIPresShell::eTreeChange,
279 0 : NS_FRAME_HAS_DIRTY_CHILDREN);
280 0 : tableFrame->SetGeometryDirty();
281 : }
282 : else {
283 0 : NS_ERROR("unexpected frame type");
284 0 : return NS_ERROR_INVALID_ARG;
285 : }
286 :
287 0 : return NS_OK;
288 : }
289 :
290 : /* virtual */ nsMargin
291 0 : nsTableRowFrame::GetUsedMargin() const
292 : {
293 0 : return nsMargin(0,0,0,0);
294 : }
295 :
296 : /* virtual */ nsMargin
297 0 : nsTableRowFrame::GetUsedBorder() const
298 : {
299 0 : return nsMargin(0,0,0,0);
300 : }
301 :
302 : /* virtual */ nsMargin
303 0 : nsTableRowFrame::GetUsedPadding() const
304 : {
305 0 : return nsMargin(0,0,0,0);
306 : }
307 :
308 : nscoord
309 0 : GetHeightOfRowsSpannedBelowFirst(nsTableCellFrame& aTableCellFrame,
310 : nsTableFrame& aTableFrame)
311 : {
312 0 : nscoord height = 0;
313 0 : nscoord cellSpacingY = aTableFrame.GetCellSpacingY();
314 0 : PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan(aTableCellFrame);
315 : // add in height of rows spanned beyond the 1st one
316 0 : nsIFrame* nextRow = aTableCellFrame.GetParent()->GetNextSibling();
317 0 : for (PRInt32 rowX = 1; ((rowX < rowSpan) && nextRow);) {
318 0 : if (nsGkAtoms::tableRowFrame == nextRow->GetType()) {
319 0 : height += nextRow->GetSize().height;
320 0 : rowX++;
321 : }
322 0 : height += cellSpacingY;
323 0 : nextRow = nextRow->GetNextSibling();
324 : }
325 0 : return height;
326 : }
327 :
328 : nsTableCellFrame*
329 0 : nsTableRowFrame::GetFirstCell()
330 : {
331 0 : nsIFrame* childFrame = mFrames.FirstChild();
332 0 : while (childFrame) {
333 0 : nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
334 0 : if (cellFrame) {
335 0 : return cellFrame;
336 : }
337 0 : childFrame = childFrame->GetNextSibling();
338 : }
339 0 : return nsnull;
340 : }
341 :
342 : /**
343 : * Post-reflow hook. This is where the table row does its post-processing
344 : */
345 : void
346 0 : nsTableRowFrame::DidResize()
347 : {
348 : // Resize and re-align the cell frames based on our row height
349 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
350 0 : nsTableIterator iter(*this);
351 0 : nsIFrame* childFrame = iter.First();
352 :
353 0 : nsHTMLReflowMetrics desiredSize;
354 0 : desiredSize.width = mRect.width;
355 0 : desiredSize.height = mRect.height;
356 0 : desiredSize.SetOverflowAreasToDesiredBounds();
357 :
358 0 : while (childFrame) {
359 0 : nsTableCellFrame *cellFrame = do_QueryFrame(childFrame);
360 0 : if (cellFrame) {
361 0 : nscoord cellHeight = mRect.height + GetHeightOfRowsSpannedBelowFirst(*cellFrame, *tableFrame);
362 :
363 : // resize the cell's height
364 0 : nsRect cellRect = cellFrame->GetRect();
365 0 : nsRect cellVisualOverflow = cellFrame->GetVisualOverflowRect();
366 0 : if (cellRect.height != cellHeight)
367 : {
368 0 : cellFrame->SetSize(nsSize(cellRect.width, cellHeight));
369 : nsTableFrame::InvalidateFrame(cellFrame, cellRect,
370 : cellVisualOverflow,
371 0 : false);
372 : }
373 :
374 : // realign cell content based on the new height. We might be able to
375 : // skip this if the height didn't change... maybe. Hard to tell.
376 0 : cellFrame->VerticallyAlignChild(mMaxCellAscent);
377 :
378 : // Always store the overflow, even if the height didn't change, since
379 : // we'll lose part of our overflow area otherwise.
380 0 : ConsiderChildOverflow(desiredSize.mOverflowAreas, cellFrame);
381 :
382 : // Note that if the cell's *content* needs to change in response
383 : // to this height, it will get a special height reflow.
384 : }
385 : // Get the next child
386 0 : childFrame = iter.Next();
387 : }
388 0 : FinishAndStoreOverflow(&desiredSize);
389 0 : if (HasView()) {
390 : nsContainerFrame::SyncFrameViewAfterReflow(PresContext(), this, GetView(),
391 0 : desiredSize.VisualOverflow(), 0);
392 : }
393 : // Let our base class do the usual work
394 0 : }
395 :
396 : // returns max-ascent amongst all cells that have 'vertical-align: baseline'
397 : // *including* cells with rowspans
398 0 : nscoord nsTableRowFrame::GetMaxCellAscent() const
399 : {
400 0 : return mMaxCellAscent;
401 : }
402 :
403 0 : nscoord nsTableRowFrame::GetRowBaseline()
404 : {
405 0 : if(mMaxCellAscent)
406 0 : return mMaxCellAscent;
407 :
408 : // If we don't have a baseline on any of the cells we go for the lowest
409 : // content edge of the inner block frames.
410 : // Every table cell has a cell frame with its border and padding. Inside
411 : // the cell is a block frame. The cell is as high as the tallest cell in
412 : // the parent row. As a consequence the block frame might not touch both
413 : // the top and the bottom padding of it parent cell frame at the same time.
414 : //
415 : // bbbbbbbbbbbbbbbbbb cell border: b
416 : // bppppppppppppppppb cell padding: p
417 : // bpxxxxxxxxxxxxxxpb inner block: x
418 : // bpx xpb
419 : // bpx xpb
420 : // bpx xpb
421 : // bpxxxxxxxxxxxxxxpb base line
422 : // bp pb
423 : // bp pb
424 : // bppppppppppppppppb
425 : // bbbbbbbbbbbbbbbbbb
426 :
427 0 : nsTableIterator iter(*this);
428 0 : nsIFrame* childFrame = iter.First();
429 0 : nscoord ascent = 0;
430 0 : while (childFrame) {
431 0 : if (IS_TABLE_CELL(childFrame->GetType())) {
432 0 : nsIFrame* firstKid = childFrame->GetFirstPrincipalChild();
433 0 : ascent = NS_MAX(ascent, firstKid->GetRect().YMost());
434 : }
435 : // Get the next child
436 0 : childFrame = iter.Next();
437 : }
438 0 : return ascent;
439 : }
440 : nscoord
441 0 : nsTableRowFrame::GetHeight(nscoord aPctBasis) const
442 : {
443 0 : nscoord height = 0;
444 0 : if ((aPctBasis > 0) && HasPctHeight()) {
445 0 : height = NSToCoordRound(GetPctHeight() * (float)aPctBasis);
446 : }
447 0 : if (HasFixedHeight()) {
448 0 : height = NS_MAX(height, GetFixedHeight());
449 : }
450 0 : return NS_MAX(height, GetContentHeight());
451 : }
452 :
453 : void
454 0 : nsTableRowFrame::ResetHeight(nscoord aFixedHeight)
455 : {
456 0 : SetHasFixedHeight(false);
457 0 : SetHasPctHeight(false);
458 0 : SetFixedHeight(0);
459 0 : SetPctHeight(0);
460 0 : SetContentHeight(0);
461 :
462 0 : if (aFixedHeight > 0) {
463 0 : SetFixedHeight(aFixedHeight);
464 : }
465 :
466 0 : mMaxCellAscent = 0;
467 0 : mMaxCellDescent = 0;
468 0 : }
469 :
470 : void
471 0 : nsTableRowFrame::UpdateHeight(nscoord aHeight,
472 : nscoord aAscent,
473 : nscoord aDescent,
474 : nsTableFrame* aTableFrame,
475 : nsTableCellFrame* aCellFrame)
476 : {
477 0 : if (!aTableFrame || !aCellFrame) {
478 0 : NS_ASSERTION(false , "invalid call");
479 0 : return;
480 : }
481 :
482 0 : if (aHeight != NS_UNCONSTRAINEDSIZE) {
483 0 : if (!(aCellFrame->HasVerticalAlignBaseline())) { // only the cell's height matters
484 0 : if (GetHeight() < aHeight) {
485 0 : PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
486 0 : if (rowSpan == 1) {
487 0 : SetContentHeight(aHeight);
488 : }
489 : }
490 : }
491 : else { // the alignment on the baseline can change the height
492 0 : NS_ASSERTION((aAscent != NS_UNCONSTRAINEDSIZE) && (aDescent != NS_UNCONSTRAINEDSIZE), "invalid call");
493 : // see if this is a long ascender
494 0 : if (mMaxCellAscent < aAscent) {
495 0 : mMaxCellAscent = aAscent;
496 : }
497 : // see if this is a long descender and without rowspan
498 0 : if (mMaxCellDescent < aDescent) {
499 0 : PRInt32 rowSpan = aTableFrame->GetEffectiveRowSpan(*aCellFrame);
500 0 : if (rowSpan == 1) {
501 0 : mMaxCellDescent = aDescent;
502 : }
503 : }
504 : // keep the tallest height in sync
505 0 : if (GetHeight() < mMaxCellAscent + mMaxCellDescent) {
506 0 : SetContentHeight(mMaxCellAscent + mMaxCellDescent);
507 : }
508 : }
509 : }
510 : }
511 :
512 : nscoord
513 0 : nsTableRowFrame::CalcHeight(const nsHTMLReflowState& aReflowState)
514 : {
515 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
516 0 : nscoord computedHeight = (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight())
517 0 : ? 0 : aReflowState.ComputedHeight();
518 0 : ResetHeight(computedHeight);
519 :
520 0 : const nsStylePosition* position = GetStylePosition();
521 0 : if (eStyleUnit_Coord == position->mHeight.GetUnit()) {
522 0 : SetFixedHeight(position->mHeight.GetCoordValue());
523 : }
524 0 : else if (eStyleUnit_Percent == position->mHeight.GetUnit()) {
525 0 : SetPctHeight(position->mHeight.GetPercentValue());
526 : }
527 : // calc() is treated like 'auto' on table rows.
528 :
529 0 : for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame;
530 : kidFrame = kidFrame->GetNextSibling()) {
531 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
532 0 : if (cellFrame) {
533 0 : nsSize desSize = cellFrame->GetDesiredSize();
534 0 : if ((NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) && !GetPrevInFlow()) {
535 0 : CalculateCellActualHeight(cellFrame, desSize.height);
536 : }
537 : // height may have changed, adjust descent to absorb any excess difference
538 : nscoord ascent;
539 0 : if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild())
540 0 : ascent = desSize.height;
541 : else
542 0 : ascent = cellFrame->GetCellBaseline();
543 0 : nscoord descent = desSize.height - ascent;
544 0 : UpdateHeight(desSize.height, ascent, descent, tableFrame, cellFrame);
545 : }
546 : }
547 0 : return GetHeight();
548 : }
549 :
550 : /**
551 : * We need a custom display item for table row backgrounds. This is only used
552 : * when the table row is the root of a stacking context (e.g., has 'opacity').
553 : * Table row backgrounds can extend beyond the row frame bounds, when
554 : * the row contains row-spanning cells.
555 : */
556 : class nsDisplayTableRowBackground : public nsDisplayTableItem {
557 : public:
558 0 : nsDisplayTableRowBackground(nsDisplayListBuilder* aBuilder,
559 : nsTableRowFrame* aFrame) :
560 0 : nsDisplayTableItem(aBuilder, aFrame) {
561 0 : MOZ_COUNT_CTOR(nsDisplayTableRowBackground);
562 0 : }
563 : #ifdef NS_BUILD_REFCNT_LOGGING
564 0 : virtual ~nsDisplayTableRowBackground() {
565 0 : MOZ_COUNT_DTOR(nsDisplayTableRowBackground);
566 0 : }
567 : #endif
568 :
569 : virtual void Paint(nsDisplayListBuilder* aBuilder,
570 : nsRenderingContext* aCtx);
571 0 : NS_DISPLAY_DECL_NAME("TableRowBackground", TYPE_TABLE_ROW_BACKGROUND)
572 : };
573 :
574 : void
575 0 : nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder,
576 : nsRenderingContext* aCtx)
577 : {
578 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
579 : TableBackgroundPainter painter(tableFrame,
580 : TableBackgroundPainter::eOrigin_TableRow,
581 : mFrame->PresContext(), *aCtx,
582 0 : mVisibleRect, ToReferenceFrame(),
583 0 : aBuilder->GetBackgroundPaintFlags());
584 0 : painter.PaintRow(static_cast<nsTableRowFrame*>(mFrame));
585 0 : }
586 :
587 : NS_IMETHODIMP
588 0 : nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
589 : const nsRect& aDirtyRect,
590 : const nsDisplayListSet& aLists)
591 : {
592 0 : nsDisplayTableItem* item = nsnull;
593 0 : if (IsVisibleInSelection(aBuilder)) {
594 0 : bool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
595 0 : if (isRoot) {
596 : // This background is created regardless of whether this frame is
597 : // visible or not. Visibility decisions are delegated to the
598 : // table background painter.
599 : // We would use nsDisplayGeneric for this rare case except that we
600 : // need the background to be larger than the row frame in some
601 : // cases.
602 0 : item = new (aBuilder) nsDisplayTableRowBackground(aBuilder, this);
603 0 : nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
604 0 : NS_ENSURE_SUCCESS(rv, rv);
605 : }
606 : }
607 0 : return nsTableFrame::DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
608 : }
609 :
610 : PRIntn
611 0 : nsTableRowFrame::GetSkipSides() const
612 : {
613 0 : PRIntn skip = 0;
614 0 : if (nsnull != GetPrevInFlow()) {
615 0 : skip |= 1 << NS_SIDE_TOP;
616 : }
617 0 : if (nsnull != GetNextInFlow()) {
618 0 : skip |= 1 << NS_SIDE_BOTTOM;
619 : }
620 0 : return skip;
621 : }
622 :
623 : // Calculate the cell's actual height given its pass2 height.
624 : // Takes into account the specified height (in the style).
625 : // Modifies the desired height that is passed in.
626 : nsresult
627 0 : nsTableRowFrame::CalculateCellActualHeight(nsTableCellFrame* aCellFrame,
628 : nscoord& aDesiredHeight)
629 : {
630 0 : nscoord specifiedHeight = 0;
631 :
632 : // Get the height specified in the style information
633 0 : const nsStylePosition* position = aCellFrame->GetStylePosition();
634 :
635 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
636 0 : PRInt32 rowSpan = tableFrame->GetEffectiveRowSpan(*aCellFrame);
637 :
638 0 : switch (position->mHeight.GetUnit()) {
639 : case eStyleUnit_Coord:
640 0 : specifiedHeight = position->mHeight.GetCoordValue();
641 0 : if (1 == rowSpan)
642 0 : SetFixedHeight(specifiedHeight);
643 0 : break;
644 : case eStyleUnit_Percent: {
645 0 : if (1 == rowSpan)
646 0 : SetPctHeight(position->mHeight.GetPercentValue());
647 : // pct heights are handled when all of the cells are finished, so don't set specifiedHeight
648 0 : break;
649 : }
650 : case eStyleUnit_Auto:
651 : default: // includes calc(), which we treat like 'auto'
652 0 : break;
653 : }
654 :
655 : // If the specified height is greater than the desired height, then use the specified height
656 0 : if (specifiedHeight > aDesiredHeight)
657 0 : aDesiredHeight = specifiedHeight;
658 :
659 0 : return NS_OK;
660 : }
661 :
662 : // Calculates the available width for the table cell based on the known
663 : // column widths taking into account column spans and column spacing
664 : static nscoord
665 0 : CalcAvailWidth(nsTableFrame& aTableFrame,
666 : nsTableCellFrame& aCellFrame,
667 : nscoord aCellSpacingX)
668 : {
669 0 : nscoord cellAvailWidth = 0;
670 : PRInt32 colIndex;
671 0 : aCellFrame.GetColIndex(colIndex);
672 0 : PRInt32 colspan = aTableFrame.GetEffectiveColSpan(aCellFrame);
673 0 : NS_ASSERTION(colspan > 0, "effective colspan should be positive");
674 :
675 0 : for (PRInt32 spanX = 0; spanX < colspan; spanX++) {
676 0 : cellAvailWidth += aTableFrame.GetColumnWidth(colIndex + spanX);
677 0 : if (spanX > 0 &&
678 0 : aTableFrame.ColumnHasCellSpacingBefore(colIndex + spanX)) {
679 0 : cellAvailWidth += aCellSpacingX;
680 : }
681 : }
682 0 : return cellAvailWidth;
683 : }
684 :
685 : nscoord
686 0 : GetSpaceBetween(PRInt32 aPrevColIndex,
687 : PRInt32 aColIndex,
688 : PRInt32 aColSpan,
689 : nsTableFrame& aTableFrame,
690 : nscoord aCellSpacingX,
691 : bool aIsLeftToRight,
692 : bool aCheckVisibility)
693 : {
694 0 : nscoord space = 0;
695 : PRInt32 colX;
696 0 : if (aIsLeftToRight) {
697 0 : for (colX = aPrevColIndex + 1; aColIndex > colX; colX++) {
698 0 : bool isCollapsed = false;
699 0 : if (!aCheckVisibility) {
700 0 : space += aTableFrame.GetColumnWidth(colX);
701 : }
702 : else {
703 0 : nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX);
704 0 : const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
705 0 : bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
706 0 : nsIFrame* cgFrame = colFrame->GetParent();
707 0 : const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility();
708 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
709 0 : groupVis->mVisible);
710 0 : isCollapsed = collapseCol || collapseGroup;
711 0 : if (!isCollapsed)
712 0 : space += aTableFrame.GetColumnWidth(colX);
713 : }
714 0 : if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
715 0 : space += aCellSpacingX;
716 : }
717 : }
718 : }
719 : else {
720 0 : PRInt32 lastCol = aColIndex + aColSpan - 1;
721 0 : for (colX = aPrevColIndex - 1; colX > lastCol; colX--) {
722 0 : bool isCollapsed = false;
723 0 : if (!aCheckVisibility) {
724 0 : space += aTableFrame.GetColumnWidth(colX);
725 : }
726 : else {
727 0 : nsTableColFrame* colFrame = aTableFrame.GetColFrame(colX);
728 0 : const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
729 0 : bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
730 0 : nsIFrame* cgFrame = colFrame->GetParent();
731 0 : const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility();
732 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
733 0 : groupVis->mVisible);
734 0 : isCollapsed = collapseCol || collapseGroup;
735 0 : if (!isCollapsed)
736 0 : space += aTableFrame.GetColumnWidth(colX);
737 : }
738 0 : if (!isCollapsed && aTableFrame.ColumnHasCellSpacingBefore(colX)) {
739 0 : space += aCellSpacingX;
740 : }
741 : }
742 : }
743 0 : return space;
744 : }
745 :
746 : // subtract the heights of aRow's prev in flows from the unpaginated height
747 : static
748 0 : nscoord CalcHeightFromUnpaginatedHeight(nsPresContext* aPresContext,
749 : nsTableRowFrame& aRow)
750 : {
751 0 : nscoord height = 0;
752 : nsTableRowFrame* firstInFlow =
753 0 : static_cast<nsTableRowFrame*>(aRow.GetFirstInFlow());
754 0 : if (firstInFlow->HasUnpaginatedHeight()) {
755 0 : height = firstInFlow->GetUnpaginatedHeight(aPresContext);
756 0 : for (nsIFrame* prevInFlow = aRow.GetPrevInFlow(); prevInFlow;
757 : prevInFlow = prevInFlow->GetPrevInFlow()) {
758 0 : height -= prevInFlow->GetSize().height;
759 : }
760 : }
761 0 : return NS_MAX(height, 0);
762 : }
763 :
764 : nsresult
765 0 : nsTableRowFrame::ReflowChildren(nsPresContext* aPresContext,
766 : nsHTMLReflowMetrics& aDesiredSize,
767 : const nsHTMLReflowState& aReflowState,
768 : nsTableFrame& aTableFrame,
769 : nsReflowStatus& aStatus)
770 : {
771 0 : aStatus = NS_FRAME_COMPLETE;
772 :
773 : // XXXldb Should we be checking constrained height instead?
774 0 : const bool isPaginated = aPresContext->IsPaginated();
775 0 : const bool borderCollapse = aTableFrame.IsBorderCollapse();
776 0 : nsresult rv = NS_OK;
777 0 : nscoord cellSpacingX = aTableFrame.GetCellSpacingX();
778 0 : PRInt32 cellColSpan = 1; // must be defined here so it's set properly for non-cell kids
779 :
780 0 : nsTableIterator iter(*this);
781 : // remember the col index of the previous cell to handle rowspans into this row
782 0 : PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 : aTableFrame.GetColCount();
783 0 : PRInt32 prevColIndex = firstPrevColIndex;
784 0 : nscoord x = 0; // running total of children x offset
785 :
786 : // This computes the max of all cell heights
787 0 : nscoord cellMaxHeight = 0;
788 :
789 : // Reflow each of our existing cell frames
790 0 : for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) {
791 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
792 0 : if (!cellFrame) {
793 : // XXXldb nsCSSFrameConstructor needs to enforce this!
794 0 : NS_NOTREACHED("yikes, a non-row child");
795 :
796 : // it's an unknown frame type, give it a generic reflow and ignore the results
797 : nsTableCellReflowState kidReflowState(aPresContext, aReflowState,
798 0 : kidFrame, nsSize(0,0), false);
799 0 : InitChildReflowState(*aPresContext, nsSize(0,0), false, kidReflowState);
800 0 : nsHTMLReflowMetrics desiredSize;
801 : nsReflowStatus status;
802 0 : ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState, 0, 0, 0, status);
803 0 : kidFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
804 :
805 0 : continue;
806 : }
807 :
808 : // See if we should only reflow the dirty child frames
809 0 : bool doReflowChild = true;
810 0 : if (!aReflowState.ShouldReflowAllKids() &&
811 0 : !aTableFrame.IsGeometryDirty() &&
812 0 : !NS_SUBTREE_DIRTY(kidFrame)) {
813 0 : if (!aReflowState.mFlags.mSpecialHeightReflow)
814 0 : doReflowChild = false;
815 : }
816 0 : else if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) {
817 : // We don't reflow a rowspan >1 cell here with a constrained height.
818 : // That happens in nsTableRowGroupFrame::SplitSpanningCells.
819 0 : if (aTableFrame.GetEffectiveRowSpan(*cellFrame) > 1) {
820 0 : doReflowChild = false;
821 : }
822 : }
823 0 : if (aReflowState.mFlags.mSpecialHeightReflow) {
824 0 : if (!isPaginated && !(cellFrame->GetStateBits() &
825 0 : NS_FRAME_CONTAINS_RELATIVE_HEIGHT)) {
826 0 : continue;
827 : }
828 : }
829 :
830 : PRInt32 cellColIndex;
831 0 : cellFrame->GetColIndex(cellColIndex);
832 0 : cellColSpan = aTableFrame.GetEffectiveColSpan(*cellFrame);
833 :
834 : // If the adjacent cell is in a prior row (because of a rowspan) add in the space
835 0 : if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
836 0 : (!iter.IsLeftToRight() && (prevColIndex != cellColIndex + cellColSpan))) {
837 : x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan, aTableFrame,
838 0 : cellSpacingX, iter.IsLeftToRight(), false);
839 : }
840 :
841 : // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into
842 0 : prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex;
843 :
844 : // Reflow the child frame
845 0 : nsRect kidRect = kidFrame->GetRect();
846 0 : nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect();
847 : bool firstReflow =
848 0 : (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
849 :
850 0 : if (doReflowChild) {
851 : // Calculate the available width for the table cell using the known column widths
852 : nscoord availCellWidth =
853 0 : CalcAvailWidth(aTableFrame, *cellFrame, cellSpacingX);
854 :
855 0 : nsHTMLReflowMetrics desiredSize;
856 :
857 : // If the avail width is not the same as last time we reflowed the cell or
858 : // the cell wants to be bigger than what was available last time or
859 : // it is a style change reflow or we are printing, then we must reflow the
860 : // cell. Otherwise we can skip the reflow.
861 : // XXXldb Why is this condition distinct from doReflowChild above?
862 0 : nsSize cellDesiredSize = cellFrame->GetDesiredSize();
863 0 : if ((availCellWidth != cellFrame->GetPriorAvailWidth()) ||
864 0 : (cellDesiredSize.width > cellFrame->GetPriorAvailWidth()) ||
865 0 : (GetStateBits() & NS_FRAME_IS_DIRTY) ||
866 : isPaginated ||
867 0 : NS_SUBTREE_DIRTY(cellFrame) ||
868 : // See if it needs a special reflow, or if it had one that we need to undo.
869 0 : (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) ||
870 0 : HasPctHeight()) {
871 : // Reflow the cell to fit the available width, height
872 : // XXX The old IR_ChildIsDirty code used availCellWidth here.
873 0 : nsSize kidAvailSize(availCellWidth, aReflowState.availableHeight);
874 :
875 : // Reflow the child
876 : nsTableCellReflowState kidReflowState(aPresContext, aReflowState,
877 0 : kidFrame, kidAvailSize, false);
878 : InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse,
879 0 : kidReflowState);
880 :
881 : nsReflowStatus status;
882 : rv = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
883 0 : x, 0, NS_FRAME_INVALIDATE_ON_MOVE, status);
884 :
885 : // allow the table to determine if/how the table needs to be rebalanced
886 : // If any of the cells are not complete, then we're not complete
887 0 : if (NS_FRAME_IS_NOT_COMPLETE(status)) {
888 0 : aStatus = NS_FRAME_NOT_COMPLETE;
889 : }
890 : }
891 : else {
892 0 : if (x != kidRect.x) {
893 0 : kidFrame->InvalidateFrameSubtree();
894 : }
895 :
896 0 : desiredSize.width = cellDesiredSize.width;
897 0 : desiredSize.height = cellDesiredSize.height;
898 0 : desiredSize.mOverflowAreas = cellFrame->GetOverflowAreas();
899 :
900 : // if we are in a floated table, our position is not yet established, so we cannot reposition our views
901 : // the containing block will do this for us after positioning the table
902 0 : if (!aTableFrame.GetStyleDisplay()->IsFloating()) {
903 : // Because we may have moved the frame we need to make sure any views are
904 : // positioned properly. We have to do this, because any one of our parent
905 : // frames could have moved and we have no way of knowing...
906 0 : nsTableFrame::RePositionViews(kidFrame);
907 : }
908 : }
909 :
910 0 : if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) {
911 0 : if (!GetPrevInFlow()) {
912 : // Calculate the cell's actual height given its pass2 height. This
913 : // function takes into account the specified height (in the style)
914 0 : CalculateCellActualHeight(cellFrame, desiredSize.height);
915 : }
916 : // height may have changed, adjust descent to absorb any excess difference
917 : nscoord ascent;
918 0 : if (!kidFrame->GetFirstPrincipalChild()->GetFirstPrincipalChild())
919 0 : ascent = desiredSize.height;
920 : else
921 0 : ascent = ((nsTableCellFrame *)kidFrame)->GetCellBaseline();
922 0 : nscoord descent = desiredSize.height - ascent;
923 0 : UpdateHeight(desiredSize.height, ascent, descent, &aTableFrame, cellFrame);
924 : }
925 : else {
926 0 : cellMaxHeight = NS_MAX(cellMaxHeight, desiredSize.height);
927 0 : PRInt32 rowSpan = aTableFrame.GetEffectiveRowSpan((nsTableCellFrame&)*kidFrame);
928 0 : if (1 == rowSpan) {
929 0 : SetContentHeight(cellMaxHeight);
930 : }
931 : }
932 :
933 : // Place the child
934 0 : desiredSize.width = availCellWidth;
935 :
936 0 : FinishReflowChild(kidFrame, aPresContext, nsnull, desiredSize, x, 0, 0);
937 :
938 : nsTableFrame::InvalidateFrame(kidFrame, kidRect, kidVisualOverflow,
939 0 : firstReflow);
940 :
941 0 : x += desiredSize.width;
942 : }
943 : else {
944 0 : if (kidRect.x != x) {
945 : // Invalidate the old position
946 0 : kidFrame->InvalidateFrameSubtree();
947 : // move to the new position
948 0 : kidFrame->SetPosition(nsPoint(x, kidRect.y));
949 0 : nsTableFrame::RePositionViews(kidFrame);
950 : // invalidate the new position
951 0 : kidFrame->InvalidateFrameSubtree();
952 : }
953 : // we need to account for the cell's width even if it isn't reflowed
954 0 : x += kidRect.width;
955 :
956 0 : if (kidFrame->GetNextInFlow()) {
957 0 : aStatus = NS_FRAME_NOT_COMPLETE;
958 : }
959 : }
960 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, kidFrame);
961 0 : x += cellSpacingX;
962 : }
963 :
964 : // just set our width to what was available. The table will calculate the width and not use our value.
965 0 : aDesiredSize.width = aReflowState.availableWidth;
966 :
967 0 : if (aReflowState.mFlags.mSpecialHeightReflow) {
968 0 : aDesiredSize.height = mRect.height;
969 : }
970 0 : else if (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight) {
971 0 : aDesiredSize.height = CalcHeight(aReflowState);
972 0 : if (GetPrevInFlow()) {
973 0 : nscoord height = CalcHeightFromUnpaginatedHeight(aPresContext, *this);
974 0 : aDesiredSize.height = NS_MAX(aDesiredSize.height, height);
975 : }
976 : else {
977 0 : if (isPaginated && HasStyleHeight()) {
978 : // set the unpaginated height so next in flows can try to honor it
979 0 : SetHasUnpaginatedHeight(true);
980 0 : SetUnpaginatedHeight(aPresContext, aDesiredSize.height);
981 : }
982 0 : if (isPaginated && HasUnpaginatedHeight()) {
983 0 : aDesiredSize.height = NS_MAX(aDesiredSize.height, GetUnpaginatedHeight(aPresContext));
984 : }
985 : }
986 : }
987 : else { // constrained height, paginated
988 : // Compute the height we should have from style (subtracting the
989 : // height from our prev-in-flows from the style height)
990 0 : nscoord styleHeight = CalcHeightFromUnpaginatedHeight(aPresContext, *this);
991 0 : if (styleHeight > aReflowState.availableHeight) {
992 0 : styleHeight = aReflowState.availableHeight;
993 0 : NS_FRAME_SET_INCOMPLETE(aStatus);
994 : }
995 0 : aDesiredSize.height = NS_MAX(cellMaxHeight, styleHeight);
996 : }
997 0 : aDesiredSize.UnionOverflowAreasWithDesiredBounds();
998 0 : FinishAndStoreOverflow(&aDesiredSize);
999 0 : return rv;
1000 : }
1001 :
1002 : /** Layout the entire row.
1003 : * This method stacks cells horizontally according to HTML 4.0 rules.
1004 : */
1005 : NS_METHOD
1006 0 : nsTableRowFrame::Reflow(nsPresContext* aPresContext,
1007 : nsHTMLReflowMetrics& aDesiredSize,
1008 : const nsHTMLReflowState& aReflowState,
1009 : nsReflowStatus& aStatus)
1010 : {
1011 0 : DO_GLOBAL_REFLOW_COUNT("nsTableRowFrame");
1012 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1013 0 : nsresult rv = NS_OK;
1014 :
1015 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1016 0 : const nsStyleVisibility* rowVis = GetStyleVisibility();
1017 0 : bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1018 0 : if (collapseRow) {
1019 0 : tableFrame->SetNeedToCollapse(true);
1020 : }
1021 :
1022 : // see if a special height reflow needs to occur due to having a pct height
1023 0 : nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
1024 :
1025 : // See if we have a cell with specified/pct height
1026 0 : InitHasCellWithStyleHeight(tableFrame);
1027 :
1028 : rv = ReflowChildren(aPresContext, aDesiredSize, aReflowState, *tableFrame,
1029 0 : aStatus);
1030 :
1031 : // just set our width to what was available. The table will calculate the width and not use our value.
1032 0 : aDesiredSize.width = aReflowState.availableWidth;
1033 :
1034 : // If our parent is in initial reflow, it'll handle invalidating our
1035 : // entire overflow rect.
1036 0 : if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1037 0 : CheckInvalidateSizeChange(aDesiredSize);
1038 : }
1039 :
1040 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
1041 0 : return rv;
1042 : }
1043 :
1044 : /**
1045 : * This function is called by the row group frame's SplitRowGroup() code when
1046 : * pushing a row frame that has cell frames that span into it. The cell frame
1047 : * should be reflowed with the specified height
1048 : */
1049 : nscoord
1050 0 : nsTableRowFrame::ReflowCellFrame(nsPresContext* aPresContext,
1051 : const nsHTMLReflowState& aReflowState,
1052 : bool aIsTopOfPage,
1053 : nsTableCellFrame* aCellFrame,
1054 : nscoord aAvailableHeight,
1055 : nsReflowStatus& aStatus)
1056 : {
1057 : // Reflow the cell frame with the specified height. Use the existing width
1058 0 : nsRect cellRect = aCellFrame->GetRect();
1059 0 : nsRect cellVisualOverflow = aCellFrame->GetVisualOverflowRect();
1060 :
1061 0 : nsSize availSize(cellRect.width, aAvailableHeight);
1062 0 : nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(this);
1063 0 : bool borderCollapse = tableFrame->IsBorderCollapse();
1064 : nsTableCellReflowState cellReflowState(aPresContext, aReflowState,
1065 0 : aCellFrame, availSize, false);
1066 0 : InitChildReflowState(*aPresContext, availSize, borderCollapse, cellReflowState);
1067 0 : cellReflowState.mFlags.mIsTopOfPage = aIsTopOfPage;
1068 :
1069 0 : nsHTMLReflowMetrics desiredSize;
1070 :
1071 : ReflowChild(aCellFrame, aPresContext, desiredSize, cellReflowState,
1072 0 : 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
1073 0 : bool fullyComplete = NS_FRAME_IS_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus);
1074 0 : if (fullyComplete) {
1075 0 : desiredSize.height = aAvailableHeight;
1076 : }
1077 0 : aCellFrame->SetSize(nsSize(cellRect.width, desiredSize.height));
1078 :
1079 : // Note: VerticallyAlignChild can affect the overflow rect.
1080 : // XXX What happens if this cell has 'vertical-align: baseline' ?
1081 : // XXX Why is it assumed that the cell's ascent hasn't changed ?
1082 0 : if (fullyComplete) {
1083 0 : aCellFrame->VerticallyAlignChild(mMaxCellAscent);
1084 : }
1085 :
1086 : nsTableFrame::InvalidateFrame(aCellFrame, cellRect,
1087 : cellVisualOverflow,
1088 0 : (aCellFrame->GetStateBits() &
1089 0 : NS_FRAME_FIRST_REFLOW) != 0);
1090 :
1091 0 : aCellFrame->DidReflow(aPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
1092 :
1093 0 : return desiredSize.height;
1094 : }
1095 :
1096 : nscoord
1097 0 : nsTableRowFrame::CollapseRowIfNecessary(nscoord aRowOffset,
1098 : nscoord aWidth,
1099 : bool aCollapseGroup,
1100 : bool& aDidCollapse)
1101 : {
1102 0 : const nsStyleVisibility* rowVis = GetStyleVisibility();
1103 0 : bool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
1104 : nsTableFrame* tableFrame = static_cast<nsTableFrame*>(
1105 0 : nsTableFrame::GetTableFrame(this)->GetFirstInFlow());
1106 0 : if (collapseRow) {
1107 0 : tableFrame->SetNeedToCollapse(true);
1108 : }
1109 :
1110 0 : if (aRowOffset != 0) {
1111 : // We're moving, so invalidate our old position
1112 0 : InvalidateFrameSubtree();
1113 : }
1114 :
1115 0 : nsRect rowRect = GetRect();
1116 0 : nsRect oldRect = rowRect;
1117 0 : nsRect oldVisualOverflow = GetVisualOverflowRect();
1118 :
1119 0 : rowRect.y -= aRowOffset;
1120 0 : rowRect.width = aWidth;
1121 0 : nsOverflowAreas overflow;
1122 0 : nscoord shift = 0;
1123 0 : nscoord cellSpacingX = tableFrame->GetCellSpacingX();
1124 0 : nscoord cellSpacingY = tableFrame->GetCellSpacingY();
1125 :
1126 0 : if (aCollapseGroup || collapseRow) {
1127 0 : nsTableCellFrame* cellFrame = GetFirstCell();
1128 0 : aDidCollapse = true;
1129 0 : shift = rowRect.height + cellSpacingY;
1130 0 : while (cellFrame) {
1131 0 : nsRect cRect = cellFrame->GetRect();
1132 : // If aRowOffset != 0, there's no point in invalidating the cells, since
1133 : // we've already invalidated our overflow area. Note that we _do_ still
1134 : // need to invalidate if our row is not moving, because the cell might
1135 : // span out of this row, so invalidating our row rect won't do enough.
1136 0 : if (aRowOffset == 0) {
1137 0 : Invalidate(cRect);
1138 : }
1139 0 : cRect.height = 0;
1140 0 : cellFrame->SetRect(cRect);
1141 0 : cellFrame = cellFrame->GetNextCell();
1142 : }
1143 0 : rowRect.height = 0;
1144 : }
1145 : else { // row is not collapsed
1146 0 : nsTableIterator iter(*this);
1147 : // remember the col index of the previous cell to handle rowspans into this
1148 : // row
1149 0 : PRInt32 firstPrevColIndex = (iter.IsLeftToRight()) ? -1 :
1150 0 : tableFrame->GetColCount();
1151 0 : PRInt32 prevColIndex = firstPrevColIndex;
1152 0 : nscoord x = 0; // running total of children x offset
1153 :
1154 0 : PRInt32 colIncrement = iter.IsLeftToRight() ? 1 : -1;
1155 :
1156 : //nscoord x = cellSpacingX;
1157 :
1158 0 : nsIFrame* kidFrame = iter.First();
1159 0 : while (kidFrame) {
1160 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1161 0 : if (cellFrame) {
1162 : PRInt32 cellColIndex;
1163 0 : cellFrame->GetColIndex(cellColIndex);
1164 0 : PRInt32 cellColSpan = tableFrame->GetEffectiveColSpan(*cellFrame);
1165 :
1166 : // If the adjacent cell is in a prior row (because of a rowspan) add in
1167 : // the space
1168 0 : if ((iter.IsLeftToRight() && (prevColIndex != (cellColIndex - 1))) ||
1169 0 : (!iter.IsLeftToRight() &&
1170 : (prevColIndex != cellColIndex + cellColSpan))) {
1171 : x += GetSpaceBetween(prevColIndex, cellColIndex, cellColSpan,
1172 0 : *tableFrame, cellSpacingX, iter.IsLeftToRight(),
1173 0 : true);
1174 : }
1175 0 : nsRect cRect(x, 0, 0, rowRect.height);
1176 :
1177 : // remember the rightmost (ltr) or leftmost (rtl) column this cell
1178 : // spans into
1179 0 : prevColIndex = (iter.IsLeftToRight()) ?
1180 0 : cellColIndex + (cellColSpan - 1) : cellColIndex;
1181 0 : PRInt32 startIndex = (iter.IsLeftToRight()) ?
1182 0 : cellColIndex : cellColIndex + (cellColSpan - 1);
1183 0 : PRInt32 actualColSpan = cellColSpan;
1184 0 : bool isVisible = false;
1185 0 : for (PRInt32 colX = startIndex; actualColSpan > 0;
1186 : colX += colIncrement, actualColSpan--) {
1187 :
1188 0 : nsTableColFrame* colFrame = tableFrame->GetColFrame(colX);
1189 0 : const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
1190 : bool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE ==
1191 0 : colVis->mVisible);
1192 0 : nsIFrame* cgFrame = colFrame->GetParent();
1193 0 : const nsStyleVisibility* groupVis = cgFrame->GetStyleVisibility();
1194 : bool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE ==
1195 0 : groupVis->mVisible);
1196 0 : bool isCollapsed = collapseCol || collapseGroup;
1197 0 : if (!isCollapsed) {
1198 0 : cRect.width += tableFrame->GetColumnWidth(colX);
1199 0 : isVisible = true;
1200 0 : if ((actualColSpan > 1)) {
1201 : nsTableColFrame* nextColFrame =
1202 0 : tableFrame->GetColFrame(colX + colIncrement);
1203 : const nsStyleVisibility* nextColVis =
1204 0 : nextColFrame->GetStyleVisibility();
1205 0 : if ( (NS_STYLE_VISIBILITY_COLLAPSE != nextColVis->mVisible) &&
1206 0 : tableFrame->ColumnHasCellSpacingBefore(colX + colIncrement)) {
1207 0 : cRect.width += cellSpacingX;
1208 : }
1209 : }
1210 : }
1211 : }
1212 0 : x += cRect.width;
1213 0 : if (isVisible)
1214 0 : x += cellSpacingX;
1215 0 : PRInt32 actualRowSpan = tableFrame->GetEffectiveRowSpan(*cellFrame);
1216 0 : nsTableRowFrame* rowFrame = GetNextRow();
1217 0 : for (actualRowSpan--; actualRowSpan > 0 && rowFrame; actualRowSpan--) {
1218 0 : const nsStyleVisibility* nextRowVis = rowFrame->GetStyleVisibility();
1219 : bool collapseNextRow = (NS_STYLE_VISIBILITY_COLLAPSE ==
1220 0 : nextRowVis->mVisible);
1221 0 : if (!collapseNextRow) {
1222 0 : nsRect nextRect = rowFrame->GetRect();
1223 0 : cRect.height += nextRect.height + cellSpacingY;
1224 : }
1225 0 : rowFrame = rowFrame->GetNextRow();
1226 : }
1227 :
1228 0 : nsRect oldCellRect = cellFrame->GetRect();
1229 0 : nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect();
1230 :
1231 0 : if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) {
1232 : // We're moving the cell. Invalidate the old overflow area
1233 0 : cellFrame->InvalidateFrameSubtree();
1234 : }
1235 :
1236 0 : cellFrame->SetRect(cRect);
1237 :
1238 : // XXXbz This looks completely bogus in the cases when we didn't
1239 : // collapse the cell!
1240 0 : nsRect cellBounds(0, 0, cRect.width, cRect.height);
1241 0 : nsOverflowAreas cellOverflow(cellBounds, cellBounds);
1242 : cellFrame->FinishAndStoreOverflow(cellOverflow,
1243 0 : nsSize(cRect.width, cRect.height));
1244 0 : nsTableFrame::RePositionViews(cellFrame);
1245 0 : ConsiderChildOverflow(overflow, cellFrame);
1246 :
1247 0 : if (aRowOffset == 0) {
1248 : nsTableFrame::InvalidateFrame(cellFrame, oldCellRect,
1249 : oldCellVisualOverflow,
1250 0 : false);
1251 : }
1252 : }
1253 0 : kidFrame = iter.Next(); // Get the next child
1254 : }
1255 : }
1256 :
1257 0 : SetRect(rowRect);
1258 0 : overflow.UnionAllWith(nsRect(0,0,rowRect.width, rowRect.height));
1259 0 : FinishAndStoreOverflow(overflow, nsSize(rowRect.width, rowRect.height));
1260 :
1261 0 : nsTableFrame::RePositionViews(this);
1262 0 : nsTableFrame::InvalidateFrame(this, oldRect, oldVisualOverflow, false);
1263 0 : return shift;
1264 : }
1265 :
1266 : /*
1267 : * The following method is called by the row group frame's SplitRowGroup()
1268 : * when it creates a continuing cell frame and wants to insert it into the
1269 : * row's child list.
1270 : */
1271 : void
1272 0 : nsTableRowFrame::InsertCellFrame(nsTableCellFrame* aFrame,
1273 : PRInt32 aColIndex)
1274 : {
1275 : // Find the cell frame where col index < aColIndex
1276 0 : nsTableCellFrame* priorCell = nsnull;
1277 0 : for (nsIFrame* child = mFrames.FirstChild(); child;
1278 : child = child->GetNextSibling()) {
1279 0 : nsTableCellFrame *cellFrame = do_QueryFrame(child);
1280 0 : if (cellFrame) {
1281 : PRInt32 colIndex;
1282 0 : cellFrame->GetColIndex(colIndex);
1283 0 : if (colIndex < aColIndex) {
1284 0 : priorCell = cellFrame;
1285 : }
1286 0 : else break;
1287 : }
1288 : }
1289 0 : mFrames.InsertFrame(this, priorCell, aFrame);
1290 0 : }
1291 :
1292 : nsIAtom*
1293 0 : nsTableRowFrame::GetType() const
1294 : {
1295 0 : return nsGkAtoms::tableRowFrame;
1296 : }
1297 :
1298 : nsTableRowFrame*
1299 0 : nsTableRowFrame::GetNextRow() const
1300 : {
1301 0 : nsIFrame* childFrame = GetNextSibling();
1302 0 : while (childFrame) {
1303 0 : nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
1304 0 : if (rowFrame) {
1305 0 : NS_ASSERTION(NS_STYLE_DISPLAY_TABLE_ROW == childFrame->GetStyleDisplay()->mDisplay, "wrong display type on rowframe");
1306 0 : return rowFrame;
1307 : }
1308 0 : childFrame = childFrame->GetNextSibling();
1309 : }
1310 0 : return nsnull;
1311 : }
1312 :
1313 0 : NS_DECLARE_FRAME_PROPERTY(RowUnpaginatedHeightProperty, nsnull)
1314 :
1315 : void
1316 0 : nsTableRowFrame::SetUnpaginatedHeight(nsPresContext* aPresContext,
1317 : nscoord aValue)
1318 : {
1319 0 : NS_ASSERTION(!GetPrevInFlow(), "program error");
1320 : // Get the property
1321 : aPresContext->PropertyTable()->
1322 0 : Set(this, RowUnpaginatedHeightProperty(), NS_INT32_TO_PTR(aValue));
1323 0 : }
1324 :
1325 : nscoord
1326 0 : nsTableRowFrame::GetUnpaginatedHeight(nsPresContext* aPresContext)
1327 : {
1328 0 : FrameProperties props = GetFirstInFlow()->Properties();
1329 0 : return NS_PTR_TO_INT32(props.Get(RowUnpaginatedHeightProperty()));
1330 : }
1331 :
1332 0 : void nsTableRowFrame::SetContinuousBCBorderWidth(PRUint8 aForSide,
1333 : BCPixelSize aPixelValue)
1334 : {
1335 0 : switch (aForSide) {
1336 : case NS_SIDE_RIGHT:
1337 0 : mRightContBorderWidth = aPixelValue;
1338 0 : return;
1339 : case NS_SIDE_TOP:
1340 0 : mTopContBorderWidth = aPixelValue;
1341 0 : return;
1342 : case NS_SIDE_LEFT:
1343 0 : mLeftContBorderWidth = aPixelValue;
1344 0 : return;
1345 : default:
1346 0 : NS_ERROR("invalid NS_SIDE arg");
1347 : }
1348 : }
1349 :
1350 : /**
1351 : * Sets the NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT bit to indicate whether
1352 : * this row has any cells that have non-auto-height. (Row-spanning
1353 : * cells are ignored.)
1354 : */
1355 0 : void nsTableRowFrame::InitHasCellWithStyleHeight(nsTableFrame* aTableFrame)
1356 : {
1357 0 : nsTableIterator iter(*this);
1358 :
1359 0 : for (nsIFrame* kidFrame = iter.First(); kidFrame; kidFrame = iter.Next()) {
1360 0 : nsTableCellFrame *cellFrame = do_QueryFrame(kidFrame);
1361 0 : if (!cellFrame) {
1362 0 : NS_NOTREACHED("Table row has a non-cell child.");
1363 0 : continue;
1364 : }
1365 : // Ignore row-spanning cells
1366 0 : const nsStyleCoord &cellHeight = cellFrame->GetStylePosition()->mHeight;
1367 0 : if (aTableFrame->GetEffectiveRowSpan(*cellFrame) == 1 &&
1368 0 : cellHeight.GetUnit() != eStyleUnit_Auto &&
1369 0 : !cellHeight.IsCalcUnit() /* calc() treated like 'auto' */) {
1370 0 : AddStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
1371 0 : return;
1372 : }
1373 : }
1374 0 : RemoveStateBits(NS_ROW_HAS_CELL_WITH_STYLE_HEIGHT);
1375 : }
1376 :
1377 : /* ----- global methods ----- */
1378 :
1379 : nsIFrame*
1380 0 : NS_NewTableRowFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
1381 : {
1382 0 : return new (aPresShell) nsTableRowFrame(aContext);
1383 : }
1384 :
1385 0 : NS_IMPL_FRAMEARENA_HELPERS(nsTableRowFrame)
1386 :
1387 : #ifdef DEBUG
1388 : NS_IMETHODIMP
1389 0 : nsTableRowFrame::GetFrameName(nsAString& aResult) const
1390 : {
1391 0 : return MakeFrameName(NS_LITERAL_STRING("TableRow"), aResult);
1392 : }
1393 : #endif
|