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