Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / layout / tables / nsTableFrame.cpp
blob197a55223e32db0f5991483982f195b90b967e85
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et tw=80: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Pierre Phaneuf <pp@ludusdesign.com>
25 * Mats Palmgren <mats.palmgren@bredband.net>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
40 #include "nsCOMPtr.h"
41 #include "nsVoidArray.h"
42 #include "nsTableFrame.h"
43 #include "nsIRenderingContext.h"
44 #include "nsStyleContext.h"
45 #include "nsStyleConsts.h"
46 #include "nsIContent.h"
47 #include "nsCellMap.h"
48 #include "nsTableCellFrame.h"
49 #include "nsHTMLParts.h"
50 #include "nsTableColFrame.h"
51 #include "nsTableColGroupFrame.h"
52 #include "nsTableRowFrame.h"
53 #include "nsTableRowGroupFrame.h"
54 #include "nsTableOuterFrame.h"
55 #include "nsTablePainter.h"
57 #include "BasicTableLayoutStrategy.h"
58 #include "FixedTableLayoutStrategy.h"
60 #include "nsPresContext.h"
61 #include "nsCSSRendering.h"
62 #include "nsStyleConsts.h"
63 #include "nsGkAtoms.h"
64 #include "nsCSSAnonBoxes.h"
65 #include "nsIPresShell.h"
66 #include "nsIDOMElement.h"
67 #include "nsIDOMHTMLElement.h"
68 #include "nsIDOMHTMLBodyElement.h"
69 #include "nsIScrollableFrame.h"
70 #include "nsFrameManager.h"
71 #include "nsCSSRendering.h"
72 #include "nsLayoutErrors.h"
73 #include "nsAutoPtr.h"
74 #include "nsCSSFrameConstructor.h"
75 #include "nsStyleSet.h"
76 #include "nsDisplayList.h"
78 /********************************************************************************
79 ** nsTableReflowState **
80 ********************************************************************************/
82 struct nsTableReflowState {
84 // the real reflow state
85 const nsHTMLReflowState& reflowState;
87 // The table's available size
88 nsSize availSize;
90 // Stationary x-offset
91 nscoord x;
93 // Running y-offset
94 nscoord y;
96 nsTableReflowState(nsPresContext& aPresContext,
97 const nsHTMLReflowState& aReflowState,
98 nsTableFrame& aTableFrame,
99 nscoord aAvailWidth,
100 nscoord aAvailHeight)
101 : reflowState(aReflowState)
103 Init(aPresContext, aTableFrame, aAvailWidth, aAvailHeight);
106 void Init(nsPresContext& aPresContext,
107 nsTableFrame& aTableFrame,
108 nscoord aAvailWidth,
109 nscoord aAvailHeight)
111 nsTableFrame* table = (nsTableFrame*)aTableFrame.GetFirstInFlow();
112 nsMargin borderPadding = table->GetChildAreaOffset(&reflowState);
113 nscoord cellSpacingX = table->GetCellSpacingX();
115 x = borderPadding.left + cellSpacingX;
116 y = borderPadding.top; //cellspacing added during reflow
118 availSize.width = aAvailWidth;
119 if (NS_UNCONSTRAINEDSIZE != availSize.width) {
120 availSize.width -= borderPadding.left + borderPadding.right
121 + (2 * cellSpacingX);
122 availSize.width = PR_MAX(0, availSize.width);
125 availSize.height = aAvailHeight;
126 if (NS_UNCONSTRAINEDSIZE != availSize.height) {
127 availSize.height -= borderPadding.top + borderPadding.bottom
128 + (2 * table->GetCellSpacingY());
129 availSize.height = PR_MAX(0, availSize.height);
133 nsTableReflowState(nsPresContext& aPresContext,
134 const nsHTMLReflowState& aReflowState,
135 nsTableFrame& aTableFrame)
136 : reflowState(aReflowState)
138 Init(aPresContext, aTableFrame, aReflowState.availableWidth, aReflowState.availableHeight);
143 /********************************************************************************
144 ** nsTableFrame **
145 ********************************************************************************/
147 struct BCPropertyData
149 BCPropertyData() { mDamageArea.x = mDamageArea.y = mDamageArea.width = mDamageArea.height =
150 mTopBorderWidth = mRightBorderWidth = mBottomBorderWidth = mLeftBorderWidth = 0; }
151 nsRect mDamageArea;
152 BCPixelSize mTopBorderWidth;
153 BCPixelSize mRightBorderWidth;
154 BCPixelSize mBottomBorderWidth;
155 BCPixelSize mLeftBorderWidth;
158 NS_IMETHODIMP
159 nsTableFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
160 nsIFrame** aProviderFrame,
161 PRBool* aIsChild)
163 // Since our parent, the table outer frame, returned this frame, we
164 // must return whatever our parent would normally have returned.
166 NS_PRECONDITION(mParent, "table constructed without outer table");
167 if (!mContent->GetParent() && !GetStyleContext()->GetPseudoType()) {
168 // We're the root. We have no style context parent.
169 *aIsChild = PR_FALSE;
170 *aProviderFrame = nsnull;
171 return NS_OK;
174 return static_cast<nsFrame*>(mParent)->
175 DoGetParentStyleContextFrame(aPresContext, aProviderFrame, aIsChild);
179 nsIAtom*
180 nsTableFrame::GetType() const
182 return nsGkAtoms::tableFrame;
186 nsTableFrame::nsTableFrame(nsStyleContext* aContext)
187 : nsHTMLContainerFrame(aContext),
188 mCellMap(nsnull),
189 mTableLayoutStrategy(nsnull)
191 mBits.mHaveReflowedColGroups = PR_FALSE;
192 mBits.mCellSpansPctCol = PR_FALSE;
193 mBits.mNeedToCalcBCBorders = PR_FALSE;
194 mBits.mIsBorderCollapse = PR_FALSE;
195 mBits.mResizedColumns = PR_FALSE; // only really matters if splitting
196 mBits.mGeometryDirty = PR_FALSE;
199 NS_IMPL_ADDREF_INHERITED(nsTableFrame, nsHTMLContainerFrame)
200 NS_IMPL_RELEASE_INHERITED(nsTableFrame, nsHTMLContainerFrame)
202 NS_IMETHODIMP
203 nsTableFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
205 NS_PRECONDITION(aInstancePtr, "null out param");
207 if (aIID.Equals(NS_GET_IID(nsITableLayout))) {
208 *aInstancePtr = static_cast<nsITableLayout*>(this);
209 return NS_OK;
212 return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
215 NS_IMETHODIMP
216 nsTableFrame::Init(nsIContent* aContent,
217 nsIFrame* aParent,
218 nsIFrame* aPrevInFlow)
220 nsresult rv;
222 // Let the base class do its processing
223 rv = nsHTMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
225 // see if border collapse is on, if so set it
226 const nsStyleTableBorder* tableStyle = GetStyleTableBorder();
227 PRBool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
228 SetBorderCollapse(borderCollapse);
229 // Create the cell map
230 if (!aPrevInFlow) {
231 mCellMap = new nsTableCellMap(*this, borderCollapse);
232 if (!mCellMap)
233 return NS_ERROR_OUT_OF_MEMORY;
234 } else {
235 mCellMap = nsnull;
238 if (aPrevInFlow) {
239 // set my width, because all frames in a table flow are the same width and
240 // code in nsTableOuterFrame depends on this being set
241 mRect.width = aPrevInFlow->GetSize().width;
243 else {
244 NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called");
245 // create the strategy
246 if (IsAutoLayout())
247 mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
248 else
249 mTableLayoutStrategy = new FixedTableLayoutStrategy(this);
250 if (!mTableLayoutStrategy)
251 return NS_ERROR_OUT_OF_MEMORY;
254 return rv;
258 nsTableFrame::~nsTableFrame()
260 if (nsnull!=mCellMap) {
261 delete mCellMap;
262 mCellMap = nsnull;
265 if (nsnull!=mTableLayoutStrategy) {
266 delete mTableLayoutStrategy;
267 mTableLayoutStrategy = nsnull;
271 void
272 nsTableFrame::Destroy()
274 mColGroups.DestroyFrames();
275 nsHTMLContainerFrame::Destroy();
278 // Make sure any views are positioned properly
279 void
280 nsTableFrame::RePositionViews(nsIFrame* aFrame)
282 nsContainerFrame::PositionFrameView(aFrame);
283 nsContainerFrame::PositionChildViews(aFrame);
286 static PRBool
287 IsRepeatedFrame(nsIFrame* kidFrame)
289 return (kidFrame->GetType() == nsGkAtoms::tableRowFrame ||
290 kidFrame->GetType() == nsGkAtoms::tableRowGroupFrame) &&
291 (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP);
294 PRBool
295 nsTableFrame::PageBreakAfter(nsIFrame& aSourceFrame,
296 nsIFrame* aNextFrame)
298 const nsStyleDisplay* display = aSourceFrame.GetStyleDisplay();
299 // don't allow a page break after a repeated element ...
300 if (display->mBreakAfter && !IsRepeatedFrame(&aSourceFrame)) {
301 return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
304 if (aNextFrame) {
305 display = aNextFrame->GetStyleDisplay();
306 // don't allow a page break before a repeated element ...
307 if (display->mBreakBefore && !IsRepeatedFrame(aNextFrame)) {
308 return !IsRepeatedFrame(&aSourceFrame); // or after
311 return PR_FALSE;
314 // XXX this needs to be cleaned up so that the frame constructor breaks out col group
315 // frames into a separate child list, bug 343048.
316 NS_IMETHODIMP
317 nsTableFrame::SetInitialChildList(nsIAtom* aListName,
318 nsIFrame* aChildList)
321 if (!mFrames.IsEmpty() || !mColGroups.IsEmpty()) {
322 // We already have child frames which means we've already been
323 // initialized
324 NS_NOTREACHED("unexpected second call to SetInitialChildList");
325 return NS_ERROR_UNEXPECTED;
327 if (aListName) {
328 // All we know about is the unnamed principal child list
329 NS_NOTREACHED("unknown frame list");
330 return NS_ERROR_INVALID_ARG;
333 nsIFrame *childFrame = aChildList;
334 nsIFrame *prevMainChild = nsnull;
335 nsIFrame *prevColGroupChild = nsnull;
336 for ( ; nsnull!=childFrame; )
338 const nsStyleDisplay* childDisplay = childFrame->GetStyleDisplay();
339 // XXX this if should go away
340 if (PR_TRUE==IsRowGroup(childDisplay->mDisplay))
342 if (mFrames.IsEmpty())
343 mFrames.SetFrames(childFrame);
344 else
345 prevMainChild->SetNextSibling(childFrame);
346 prevMainChild = childFrame;
348 else if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay)
350 NS_ASSERTION(nsGkAtoms::tableColGroupFrame == childFrame->GetType(),
351 "This is not a colgroup");
352 if (mColGroups.IsEmpty())
353 mColGroups.SetFrames(childFrame);
354 else
355 prevColGroupChild->SetNextSibling(childFrame);
356 prevColGroupChild = childFrame;
358 else
359 { // unknown frames go on the main list for now
360 if (mFrames.IsEmpty())
361 mFrames.SetFrames(childFrame);
362 else
363 prevMainChild->SetNextSibling(childFrame);
364 prevMainChild = childFrame;
366 nsIFrame *prevChild = childFrame;
367 childFrame = childFrame->GetNextSibling();
368 prevChild->SetNextSibling(nsnull);
370 if (nsnull!=prevMainChild)
371 prevMainChild->SetNextSibling(nsnull);
372 if (nsnull!=prevColGroupChild)
373 prevColGroupChild->SetNextSibling(nsnull);
375 // If we have a prev-in-flow, then we're a table that has been split and
376 // so don't treat this like an append
377 if (!GetPrevInFlow()) {
378 // process col groups first so that real cols get constructed before
379 // anonymous ones due to cells in rows.
380 InsertColGroups(0, mColGroups.FirstChild());
381 AppendRowGroups(mFrames.FirstChild());
382 // calc collapsing borders
383 if (IsBorderCollapse()) {
384 nsRect damageArea(0, 0, GetColCount(), GetRowCount());
385 SetBCDamageArea(damageArea);
389 return NS_OK;
392 /* virtual */ PRBool
393 nsTableFrame::IsContainingBlock() const
395 return PR_TRUE;
398 void nsTableFrame::AttributeChangedFor(nsIFrame* aFrame,
399 nsIContent* aContent,
400 nsIAtom* aAttribute)
402 if (IS_TABLE_CELL(aFrame->GetType())) {
403 if ((nsGkAtoms::rowspan == aAttribute) ||
404 (nsGkAtoms::colspan == aAttribute)) {
405 nsTableCellMap* cellMap = GetCellMap();
406 if (cellMap) {
407 // for now just remove the cell from the map and reinsert it
408 nsTableCellFrame* cellFrame = (nsTableCellFrame*)aFrame;
409 PRInt32 rowIndex, colIndex;
410 cellFrame->GetRowIndex(rowIndex);
411 cellFrame->GetColIndex(colIndex);
412 RemoveCell(cellFrame, rowIndex);
413 nsAutoVoidArray cells;
414 cells.AppendElement(cellFrame);
415 InsertCells(cells, rowIndex, colIndex - 1);
417 // XXX Should this use eStyleChange? It currently doesn't need
418 // to, but it might given more optimization.
419 PresContext()->PresShell()->
420 FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
427 /* ****** CellMap methods ******* */
429 /* return the effective col count */
430 PRInt32 nsTableFrame::GetEffectiveColCount() const
432 PRInt32 colCount = GetColCount();
433 if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto) {
434 nsTableCellMap* cellMap = GetCellMap();
435 if (!cellMap) {
436 return 0;
438 // don't count cols at the end that don't have originating cells
439 for (PRInt32 colX = colCount - 1; colX >= 0; colX--) {
440 if (cellMap->GetNumCellsOriginatingInCol(colX) > 0) {
441 break;
443 colCount--;
446 return colCount;
449 PRInt32 nsTableFrame::GetIndexOfLastRealCol()
451 PRInt32 numCols = mColFrames.Count();
452 if (numCols > 0) {
453 for (PRInt32 colX = numCols - 1; colX >= 0; colX--) {
454 nsTableColFrame* colFrame = GetColFrame(colX);
455 if (colFrame) {
456 if (eColAnonymousCell != colFrame->GetColType()) {
457 return colX;
462 return -1;
465 nsTableColFrame*
466 nsTableFrame::GetColFrame(PRInt32 aColIndex) const
468 NS_ASSERTION(!GetPrevInFlow(), "GetColFrame called on next in flow");
469 PRInt32 numCols = mColFrames.Count();
470 if ((aColIndex >= 0) && (aColIndex < numCols)) {
471 return (nsTableColFrame *)mColFrames.ElementAt(aColIndex);
473 else {
474 NS_ERROR("invalid col index");
475 return nsnull;
479 PRInt32 nsTableFrame::GetEffectiveRowSpan(PRInt32 aRowIndex,
480 const nsTableCellFrame& aCell) const
482 nsTableCellMap* cellMap = GetCellMap();
483 NS_PRECONDITION (nsnull != cellMap, "bad call, cellMap not yet allocated.");
485 PRInt32 colIndex;
486 aCell.GetColIndex(colIndex);
487 return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex);
490 PRInt32 nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
491 nsCellMap* aCellMap)
493 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
495 PRInt32 colIndex, rowIndex;
496 aCell.GetColIndex(colIndex);
497 aCell.GetRowIndex(rowIndex);
499 if (aCellMap)
500 return aCellMap->GetRowSpan(rowIndex, colIndex, PR_TRUE);
501 else
502 return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
505 PRInt32 nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
506 nsCellMap* aCellMap) const
508 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
510 PRInt32 colIndex, rowIndex;
511 aCell.GetColIndex(colIndex);
512 aCell.GetRowIndex(rowIndex);
513 PRBool ignore;
515 if (aCellMap)
516 return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore);
517 else
518 return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
521 PRBool nsTableFrame::HasMoreThanOneCell(PRInt32 aRowIndex) const
523 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
524 return tableCellMap->HasMoreThanOneCell(aRowIndex);
527 PRInt32 nsTableFrame::GetEffectiveCOLSAttribute()
529 NS_PRECONDITION (GetCellMap(), "null cellMap.");
531 PRInt32 result;
532 result = GetStyleTable()->mCols;
533 PRInt32 numCols = GetColCount();
534 if (result > numCols)
535 result = numCols;
536 return result;
539 void nsTableFrame::AdjustRowIndices(PRInt32 aRowIndex,
540 PRInt32 aAdjustment)
542 // Iterate over the row groups and adjust the row indices of all rows
543 // whose index is >= aRowIndex.
544 RowGroupArray rowGroups;
545 OrderRowGroups(rowGroups);
547 for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
548 rowGroups[rgX]->AdjustRowIndices(aRowIndex, aAdjustment);
553 void nsTableFrame::ResetRowIndices(nsIFrame* aFirstRowGroupFrame,
554 nsIFrame* aLastRowGroupFrame)
556 // Iterate over the row groups and adjust the row indices of all rows
557 // omit the rowgroups that will be inserted later
558 RowGroupArray rowGroups;
559 OrderRowGroups(rowGroups);
561 PRInt32 rowIndex = 0;
562 nsTableRowGroupFrame* newRgFrame = nsnull;
563 nsIFrame* omitRgFrame = aFirstRowGroupFrame;
564 if (omitRgFrame) {
565 newRgFrame = GetRowGroupFrame(omitRgFrame);
566 if (omitRgFrame == aLastRowGroupFrame)
567 omitRgFrame = nsnull;
570 for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
571 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
572 if (rgFrame == newRgFrame) {
573 // omit the new rowgroup
574 if (omitRgFrame) {
575 omitRgFrame = omitRgFrame->GetNextSibling();
576 if (omitRgFrame) {
577 newRgFrame = GetRowGroupFrame(omitRgFrame);
578 if (omitRgFrame == aLastRowGroupFrame)
579 omitRgFrame = nsnull;
583 else {
584 nsIFrame* rowFrame = rgFrame->GetFirstChild(nsnull);
585 for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
586 if (NS_STYLE_DISPLAY_TABLE_ROW==rowFrame->GetStyleDisplay()->mDisplay) {
587 ((nsTableRowFrame *)rowFrame)->SetRowIndex(rowIndex);
588 rowIndex++;
594 void nsTableFrame::InsertColGroups(PRInt32 aStartColIndex,
595 nsIFrame* aFirstFrame,
596 nsIFrame* aLastFrame)
598 PRInt32 colIndex = aStartColIndex;
599 nsTableColGroupFrame* firstColGroupToReset = nsnull;
600 nsIFrame* kidFrame = aFirstFrame;
601 PRBool didLastFrame = PR_FALSE;
602 while (kidFrame) {
603 if (nsGkAtoms::tableColGroupFrame == kidFrame->GetType()) {
604 if (didLastFrame) {
605 firstColGroupToReset = (nsTableColGroupFrame*)kidFrame;
606 break;
608 else {
609 nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)kidFrame;
610 cgFrame->SetStartColumnIndex(colIndex);
611 nsIFrame* firstCol = kidFrame->GetFirstChild(nsnull);
612 cgFrame->AddColsToTable(colIndex, PR_FALSE, firstCol);
613 PRInt32 numCols = cgFrame->GetColCount();
614 colIndex += numCols;
617 if (kidFrame == aLastFrame) {
618 didLastFrame = PR_TRUE;
620 kidFrame = kidFrame->GetNextSibling();
623 if (firstColGroupToReset) {
624 nsTableColGroupFrame::ResetColIndices(firstColGroupToReset, colIndex);
628 void nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
629 PRInt32 aColIndex)
631 mColFrames.InsertElementAt(&aColFrame, aColIndex);
632 nsTableColType insertedColType = aColFrame.GetColType();
633 PRInt32 numCacheCols = mColFrames.Count();
634 nsTableCellMap* cellMap = GetCellMap();
635 if (cellMap) {
636 PRInt32 numMapCols = cellMap->GetColCount();
637 if (numCacheCols > numMapCols) {
638 PRBool removedFromCache = PR_FALSE;
639 if (eColAnonymousCell != insertedColType) {
640 nsTableColFrame* lastCol = (nsTableColFrame *)mColFrames.ElementAt(numCacheCols - 1);
641 if (lastCol) {
642 nsTableColType lastColType = lastCol->GetColType();
643 if (eColAnonymousCell == lastColType) {
644 // remove the col from the cache
645 mColFrames.RemoveElementAt(numCacheCols - 1);
646 // remove the col from the eColGroupAnonymousCell col group
647 nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
648 if (lastColGroup) {
649 lastColGroup->RemoveChild(*lastCol, PR_FALSE);
651 // remove the col group if it is empty
652 if (lastColGroup->GetColCount() <= 0) {
653 mColGroups.DestroyFrame((nsIFrame*)lastColGroup);
655 removedFromCache = PR_TRUE;
659 if (!removedFromCache) {
660 cellMap->AddColsAtEnd(1);
664 // for now, just bail and recalc all of the collapsing borders
665 if (IsBorderCollapse()) {
666 nsRect damageArea(0, 0, PR_MAX(1, GetColCount()), PR_MAX(1, GetRowCount()));
667 SetBCDamageArea(damageArea);
671 void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
672 PRInt32 aColIndex,
673 PRBool aRemoveFromCache,
674 PRBool aRemoveFromCellMap)
676 if (aRemoveFromCache) {
677 mColFrames.RemoveElementAt(aColIndex);
679 if (aRemoveFromCellMap) {
680 nsTableCellMap* cellMap = GetCellMap();
681 if (cellMap) {
682 CreateAnonymousColFrames(1, eColAnonymousCell, PR_TRUE);
685 // for now, just bail and recalc all of the collapsing borders
686 if (IsBorderCollapse()) {
687 nsRect damageArea(0, 0, GetColCount(), GetRowCount());
688 SetBCDamageArea(damageArea);
692 /** Get the cell map for this table frame. It is not always mCellMap.
693 * Only the firstInFlow has a legit cell map
695 nsTableCellMap* nsTableFrame::GetCellMap() const
697 nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
698 return firstInFlow->mCellMap;
701 // XXX this needs to be moved to nsCSSFrameConstructor
702 nsTableColGroupFrame*
703 nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
705 nsIContent* colGroupContent = GetContent();
706 nsPresContext* presContext = PresContext();
707 nsIPresShell *shell = presContext->PresShell();
709 nsRefPtr<nsStyleContext> colGroupStyle;
710 colGroupStyle = shell->StyleSet()->ResolvePseudoStyleFor(colGroupContent,
711 nsCSSAnonBoxes::tableColGroup,
712 mStyleContext);
713 // Create a col group frame
714 nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle);
715 if (newFrame) {
716 ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
717 newFrame->Init(colGroupContent, this, nsnull);
719 return (nsTableColGroupFrame *)newFrame;
722 void
723 nsTableFrame::CreateAnonymousColFrames(PRInt32 aNumColsToAdd,
724 nsTableColType aColType,
725 PRBool aDoAppend,
726 nsIFrame* aPrevColIn)
728 NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
730 // get the last col group frame
731 nsTableColGroupFrame* colGroupFrame = nsnull;
732 nsIFrame* childFrame = mColGroups.FirstChild();
733 while (childFrame) {
734 if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) {
735 colGroupFrame = (nsTableColGroupFrame *)childFrame;
737 childFrame = childFrame->GetNextSibling();
740 nsTableColGroupType lastColGroupType = eColGroupContent;
741 nsTableColGroupType newColGroupType = eColGroupContent;
742 if (colGroupFrame) {
743 lastColGroupType = colGroupFrame->GetColType();
745 if (eColAnonymousCell == aColType) {
746 if (eColGroupAnonymousCell != lastColGroupType) {
747 newColGroupType = eColGroupAnonymousCell;
750 else {
751 NS_ASSERTION(PR_FALSE, "CreateAnonymousColFrames called incorrectly");
752 return;
755 if (eColGroupContent != newColGroupType) {
756 PRInt32 colIndex = (colGroupFrame) ? colGroupFrame->GetStartColumnIndex() + colGroupFrame->GetColCount()
757 : 0;
758 colGroupFrame = CreateAnonymousColGroupFrame(newColGroupType);
759 if (!colGroupFrame) {
760 return;
762 mColGroups.AppendFrame(this, colGroupFrame); // add the new frame to the child list
763 colGroupFrame->SetStartColumnIndex(colIndex);
766 nsIFrame* prevCol = (aDoAppend) ? colGroupFrame->GetChildList().LastChild() : aPrevColIn;
768 nsIFrame* firstNewFrame;
769 CreateAnonymousColFrames(colGroupFrame, aNumColsToAdd, aColType,
770 PR_TRUE, prevCol, &firstNewFrame);
773 // XXX this needs to be moved to nsCSSFrameConstructor
774 // Right now it only creates the col frames at the end
775 void
776 nsTableFrame::CreateAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
777 PRInt32 aNumColsToAdd,
778 nsTableColType aColType,
779 PRBool aAddToColGroupAndTable,
780 nsIFrame* aPrevFrameIn,
781 nsIFrame** aFirstNewFrame)
783 NS_PRECONDITION(aColGroupFrame, "null frame");
784 NS_PRECONDITION(aColType != eColAnonymousCol, "Shouldn't happen");
786 *aFirstNewFrame = nsnull;
787 nsIFrame* lastColFrame = nsnull;
788 nsPresContext* presContext = PresContext();
789 nsIPresShell *shell = presContext->PresShell();
791 // Get the last col frame
792 nsIFrame* childFrame = aColGroupFrame->GetFirstChild(nsnull);
793 while (childFrame) {
794 if (nsGkAtoms::tableColFrame == childFrame->GetType()) {
795 lastColFrame = (nsTableColGroupFrame *)childFrame;
797 childFrame = childFrame->GetNextSibling();
800 PRInt32 startIndex = mColFrames.Count();
801 PRInt32 lastIndex = startIndex + aNumColsToAdd - 1;
803 for (PRInt32 childX = startIndex; childX <= lastIndex; childX++) {
804 nsIContent* iContent;
805 nsRefPtr<nsStyleContext> styleContext;
806 nsStyleContext* parentStyleContext;
808 // all anonymous cols that we create here use a pseudo style context of the
809 // col group
810 iContent = aColGroupFrame->GetContent();
811 parentStyleContext = aColGroupFrame->GetStyleContext();
812 styleContext = shell->StyleSet()->ResolvePseudoStyleFor(iContent,
813 nsCSSAnonBoxes::tableCol,
814 parentStyleContext);
815 // ASSERTION to check for bug 54454 sneaking back in...
816 NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
818 // create the new col frame
819 nsIFrame* colFrame = NS_NewTableColFrame(shell, styleContext);
820 ((nsTableColFrame *) colFrame)->SetColType(aColType);
821 colFrame->Init(iContent, aColGroupFrame, nsnull);
822 colFrame->SetInitialChildList(nsnull, nsnull);
824 // Add the col to the sibling chain
825 if (lastColFrame) {
826 lastColFrame->SetNextSibling(colFrame);
828 lastColFrame = colFrame;
829 if (childX == startIndex) {
830 *aFirstNewFrame = colFrame;
833 if (aAddToColGroupAndTable) {
834 nsFrameList& cols = aColGroupFrame->GetChildList();
835 // the chain already exists, now add it to the col group child list
836 if (!aPrevFrameIn) {
837 cols.AppendFrames(aColGroupFrame, *aFirstNewFrame);
839 // get the starting col index in the cache
840 PRInt32 startColIndex = aColGroupFrame->GetStartColumnIndex();
841 if (aPrevFrameIn) {
842 nsTableColFrame* colFrame =
843 (nsTableColFrame*)nsTableFrame::GetFrameAtOrBefore((nsIFrame*) aColGroupFrame, aPrevFrameIn,
844 nsGkAtoms::tableColFrame);
845 if (colFrame) {
846 startColIndex = colFrame->GetColIndex() + 1;
849 aColGroupFrame->AddColsToTable(startColIndex, PR_TRUE,
850 *aFirstNewFrame, lastColFrame);
854 void
855 nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
857 PRInt32 numColsInMap = GetColCount();
858 PRInt32 numColsInCache = mColFrames.Count();
859 PRInt32 numColsToAdd = numColsInMap - numColsInCache;
860 if (numColsToAdd > 0) {
861 // this sets the child list, updates the col cache and cell map
862 CreateAnonymousColFrames(numColsToAdd, eColAnonymousCell, PR_TRUE);
864 if (numColsToAdd < 0) {
865 PRInt32 numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
866 // if the cell map has fewer cols than the cache, correct it
867 if (numColsNotRemoved > 0) {
868 aCellMap->AddColsAtEnd(numColsNotRemoved);
871 if (numColsToAdd && HasZeroColSpans()) {
872 SetNeedColSpanExpansion(PR_TRUE);
874 if (NeedColSpanExpansion()) {
875 // This flag can be set in two ways -- either by changing
876 // the number of columns (that happens in the block above),
877 // or by adding a cell with colspan="0" to the cellmap. To
878 // handle the latter case we need to explicitly check the
879 // flag here -- it may be set even if the number of columns
880 // did not change.
882 // @see nsCellMap::AppendCell
884 aCellMap->ExpandZeroColSpans();
888 void
889 nsTableFrame::DidResizeColumns()
891 NS_PRECONDITION(!GetPrevInFlow(),
892 "should only be called on first-in-flow");
893 if (mBits.mResizedColumns)
894 return; // already marked
896 for (nsTableFrame *f = this; f;
897 f = static_cast<nsTableFrame*>(f->GetNextInFlow()))
898 f->mBits.mResizedColumns = PR_TRUE;
901 void
902 nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
903 PRInt32 aRowIndex)
905 nsTableCellMap* cellMap = GetCellMap();
906 if (cellMap) {
907 nsRect damageArea(0,0,0,0);
908 cellMap->AppendCell(aCellFrame, aRowIndex, PR_TRUE, damageArea);
909 MatchCellMapToColCache(cellMap);
910 if (IsBorderCollapse()) {
911 SetBCDamageArea(damageArea);
916 void nsTableFrame::InsertCells(nsVoidArray& aCellFrames,
917 PRInt32 aRowIndex,
918 PRInt32 aColIndexBefore)
920 nsTableCellMap* cellMap = GetCellMap();
921 if (cellMap) {
922 nsRect damageArea(0,0,0,0);
923 cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
924 MatchCellMapToColCache(cellMap);
925 if (IsBorderCollapse()) {
926 SetBCDamageArea(damageArea);
931 // this removes the frames from the col group and table, but not the cell map
932 PRInt32
933 nsTableFrame::DestroyAnonymousColFrames(PRInt32 aNumFrames)
935 // only remove cols that are of type eTypeAnonymous cell (they are at the end)
936 PRInt32 endIndex = mColFrames.Count() - 1;
937 PRInt32 startIndex = (endIndex - aNumFrames) + 1;
938 PRInt32 numColsRemoved = 0;
939 for (PRInt32 colX = endIndex; colX >= startIndex; colX--) {
940 nsTableColFrame* colFrame = GetColFrame(colX);
941 if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
942 nsTableColGroupFrame* cgFrame =
943 static_cast<nsTableColGroupFrame*>(colFrame->GetParent());
944 // remove the frame from the colgroup
945 cgFrame->RemoveChild(*colFrame, PR_FALSE);
946 // remove the frame from the cache, but not the cell map
947 RemoveCol(nsnull, colX, PR_TRUE, PR_FALSE);
948 numColsRemoved++;
950 else {
951 break;
954 return (aNumFrames - numColsRemoved);
957 void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
958 PRInt32 aRowIndex)
960 nsTableCellMap* cellMap = GetCellMap();
961 if (cellMap) {
962 nsRect damageArea(0,0,0,0);
963 cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
964 MatchCellMapToColCache(cellMap);
965 if (IsBorderCollapse()) {
966 SetBCDamageArea(damageArea);
971 PRInt32
972 nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame& aRowGroupFrame)
974 RowGroupArray orderedRowGroups;
975 OrderRowGroups(orderedRowGroups);
977 PRInt32 rowIndex = 0;
978 for (PRUint32 rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
979 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
980 if (rgFrame == &aRowGroupFrame) {
981 break;
983 PRInt32 numRows = rgFrame->GetRowCount();
984 rowIndex += numRows;
986 return rowIndex;
989 // this cannot extend beyond a single row group
990 void nsTableFrame::AppendRows(nsTableRowGroupFrame& aRowGroupFrame,
991 PRInt32 aRowIndex,
992 nsVoidArray& aRowFrames)
994 nsTableCellMap* cellMap = GetCellMap();
995 if (cellMap) {
996 PRInt32 absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
997 InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, PR_TRUE);
1001 PRInt32
1002 nsTableFrame::InsertRow(nsTableRowGroupFrame& aRowGroupFrame,
1003 nsIFrame& aRowFrame,
1004 PRInt32 aRowIndex,
1005 PRBool aConsiderSpans)
1007 nsAutoVoidArray rows;
1008 rows.AppendElement(&aRowFrame);
1009 return InsertRows(aRowGroupFrame, rows, aRowIndex, aConsiderSpans);
1012 // this cannot extend beyond a single row group
1013 PRInt32
1014 nsTableFrame::InsertRows(nsTableRowGroupFrame& aRowGroupFrame,
1015 nsVoidArray& aRowFrames,
1016 PRInt32 aRowIndex,
1017 PRBool aConsiderSpans)
1019 #ifdef DEBUG_TABLE_CELLMAP
1020 printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
1021 Dump(PR_TRUE, PR_FALSE, PR_TRUE);
1022 #endif
1024 PRInt32 numColsToAdd = 0;
1025 nsTableCellMap* cellMap = GetCellMap();
1026 if (cellMap) {
1027 nsRect damageArea(0,0,0,0);
1028 PRInt32 origNumRows = cellMap->GetRowCount();
1029 PRInt32 numNewRows = aRowFrames.Count();
1030 cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
1031 MatchCellMapToColCache(cellMap);
1032 if (aRowIndex < origNumRows) {
1033 AdjustRowIndices(aRowIndex, numNewRows);
1035 // assign the correct row indices to the new rows. If they were adjusted above
1036 // it may not have been done correctly because each row is constructed with index 0
1037 for (PRInt32 rowX = 0; rowX < numNewRows; rowX++) {
1038 nsTableRowFrame* rowFrame = (nsTableRowFrame *) aRowFrames.ElementAt(rowX);
1039 rowFrame->SetRowIndex(aRowIndex + rowX);
1041 if (IsBorderCollapse()) {
1042 SetBCDamageArea(damageArea);
1045 #ifdef DEBUG_TABLE_CELLMAP
1046 printf("=== insertRowsAfter \n");
1047 Dump(PR_TRUE, PR_FALSE, PR_TRUE);
1048 #endif
1050 return numColsToAdd;
1053 // this cannot extend beyond a single row group
1054 void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
1055 PRInt32 aNumRowsToRemove,
1056 PRBool aConsiderSpans)
1058 #ifdef TBD_OPTIMIZATION
1059 // decide if we need to rebalance. we have to do this here because the row group
1060 // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
1061 PRBool stopTelling = PR_FALSE;
1062 for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
1063 kidFrame = kidFrame->GetNextSibling()) {
1064 if (IS_TABLE_CELL(kidFrame->GetType())) {
1065 nsTableCellFrame* cellFrame = (nsTableCellFrame*)kidFrame;
1066 stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(),
1067 cellFrame->GetMaximumWidth(), PR_TRUE);
1070 // XXX need to consider what happens if there are cells that have rowspans
1071 // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
1072 #endif
1074 PRInt32 firstRowIndex = aFirstRowFrame.GetRowIndex();
1075 #ifdef DEBUG_TABLE_CELLMAP
1076 printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
1077 Dump(PR_TRUE, PR_FALSE, PR_TRUE);
1078 #endif
1079 nsTableCellMap* cellMap = GetCellMap();
1080 if (cellMap) {
1081 nsRect damageArea(0,0,0,0);
1082 cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
1083 MatchCellMapToColCache(cellMap);
1084 if (IsBorderCollapse()) {
1085 SetBCDamageArea(damageArea);
1088 AdjustRowIndices(firstRowIndex, -aNumRowsToRemove);
1089 #ifdef DEBUG_TABLE_CELLMAP
1090 printf("=== removeRowsAfter\n");
1091 Dump(PR_TRUE, PR_TRUE, PR_TRUE);
1092 #endif
1095 void nsTableFrame::AppendRowGroups(nsIFrame* aFirstRowGroupFrame)
1097 if (aFirstRowGroupFrame) {
1098 nsTableCellMap* cellMap = GetCellMap();
1099 if (cellMap) {
1100 nsFrameList newList(aFirstRowGroupFrame);
1101 InsertRowGroups(aFirstRowGroupFrame, newList.LastChild());
1106 nsTableRowGroupFrame*
1107 nsTableFrame::GetRowGroupFrame(nsIFrame* aFrame,
1108 nsIAtom* aFrameTypeIn)
1110 nsIFrame* rgFrame = nsnull;
1111 nsIAtom* frameType = aFrameTypeIn;
1112 if (!aFrameTypeIn) {
1113 frameType = aFrame->GetType();
1115 if (nsGkAtoms::tableRowGroupFrame == frameType) {
1116 rgFrame = aFrame;
1118 else if (nsGkAtoms::scrollFrame == frameType) {
1119 nsIScrollableFrame* scrollable = nsnull;
1120 nsresult rv = CallQueryInterface(aFrame, &scrollable);
1121 if (NS_SUCCEEDED(rv) && (scrollable)) {
1122 nsIFrame* scrolledFrame = scrollable->GetScrolledFrame();
1123 if (scrolledFrame) {
1124 if (nsGkAtoms::tableRowGroupFrame == scrolledFrame->GetType()) {
1125 rgFrame = scrolledFrame;
1130 return (nsTableRowGroupFrame*)rgFrame;
1133 // collect the rows ancestors of aFrame
1134 PRInt32
1135 nsTableFrame::CollectRows(nsIFrame* aFrame,
1136 nsVoidArray& aCollection)
1138 if (!aFrame) return 0;
1139 PRInt32 numRows = 0;
1140 nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aFrame);
1141 if (rgFrame) {
1142 nsIFrame* childFrame = rgFrame->GetFirstChild(nsnull);
1143 while (childFrame) {
1144 if (nsGkAtoms::tableRowFrame == childFrame->GetType()) {
1145 aCollection.AppendElement(childFrame);
1146 numRows++;
1148 else {
1149 numRows += CollectRows(childFrame, aCollection);
1151 childFrame = childFrame->GetNextSibling();
1154 return numRows;
1157 void
1158 nsTableFrame::InsertRowGroups(nsIFrame* aFirstRowGroupFrame,
1159 nsIFrame* aLastRowGroupFrame)
1161 #ifdef DEBUG_TABLE_CELLMAP
1162 printf("=== insertRowGroupsBefore\n");
1163 Dump(PR_TRUE, PR_FALSE, PR_TRUE);
1164 #endif
1165 nsTableCellMap* cellMap = GetCellMap();
1166 if (cellMap) {
1167 RowGroupArray orderedRowGroups;
1168 OrderRowGroups(orderedRowGroups);
1170 nsAutoVoidArray rows;
1171 // Loop over the rowgroups and check if some of them are new, if they are
1172 // insert cellmaps in the order that is predefined by OrderRowGroups,
1173 PRUint32 rgIndex;
1174 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
1175 nsIFrame* kidFrame = aFirstRowGroupFrame;
1176 while (kidFrame) {
1177 nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
1179 if (orderedRowGroups[rgIndex] == rgFrame) {
1180 nsTableRowGroupFrame* priorRG =
1181 (0 == rgIndex) ? nsnull : orderedRowGroups[rgIndex - 1];
1182 // create and add the cell map for the row group
1183 cellMap->InsertGroupCellMap(*rgFrame, priorRG);
1185 break;
1187 else {
1188 if (kidFrame == aLastRowGroupFrame) {
1189 break;
1191 kidFrame = kidFrame->GetNextSibling();
1195 cellMap->Synchronize(this);
1196 ResetRowIndices(aFirstRowGroupFrame, aLastRowGroupFrame);
1198 //now that the cellmaps are reordered too insert the rows
1199 for (rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
1200 nsIFrame* kidFrame = aFirstRowGroupFrame;
1201 while (kidFrame) {
1202 nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
1204 if (orderedRowGroups[rgIndex] == rgFrame) {
1205 nsTableRowGroupFrame* priorRG =
1206 (0 == rgIndex) ? nsnull : orderedRowGroups[rgIndex - 1];
1207 // collect the new row frames in an array and add them to the table
1208 PRInt32 numRows = CollectRows(kidFrame, rows);
1209 if (numRows > 0) {
1210 PRInt32 rowIndex = 0;
1211 if (priorRG) {
1212 PRInt32 priorNumRows = priorRG->GetRowCount();
1213 rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
1215 InsertRows(*rgFrame, rows, rowIndex, PR_TRUE);
1216 rows.Clear();
1218 break;
1220 else {
1221 if (kidFrame == aLastRowGroupFrame) {
1222 break;
1224 kidFrame = kidFrame->GetNextSibling();
1230 #ifdef DEBUG_TABLE_CELLMAP
1231 printf("=== insertRowGroupsAfter\n");
1232 Dump(PR_TRUE, PR_TRUE, PR_TRUE);
1233 #endif
1237 /////////////////////////////////////////////////////////////////////////////
1238 // Child frame enumeration
1240 nsIFrame*
1241 nsTableFrame::GetFirstChild(nsIAtom* aListName) const
1243 if (aListName == nsGkAtoms::colGroupList) {
1244 return mColGroups.FirstChild();
1247 return nsHTMLContainerFrame::GetFirstChild(aListName);
1250 nsIAtom*
1251 nsTableFrame::GetAdditionalChildListName(PRInt32 aIndex) const
1253 if (aIndex == NS_TABLE_FRAME_COLGROUP_LIST_INDEX) {
1254 return nsGkAtoms::colGroupList;
1256 if (aIndex == NS_TABLE_FRAME_OVERFLOW_LIST_INDEX) {
1257 return nsGkAtoms::overflowList;
1259 return nsnull;
1262 nsRect
1263 nsDisplayTableItem::GetBounds(nsDisplayListBuilder* aBuilder) {
1264 return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
1267 PRBool
1268 nsDisplayTableItem::IsVaryingRelativeToMovingFrame(nsDisplayListBuilder* aBuilder)
1270 if (!mPartHasFixedBackground)
1271 return PR_FALSE;
1273 // aAncestorFrame is the frame that is going to be moved.
1274 // Check if mFrame is equal to aAncestorFrame or aAncestorFrame is an
1275 // ancestor of mFrame in the same document. If this is true, mFrame
1276 // will move relative to its viewport, which means this display item will
1277 // change when it is moved. If they are in different documents, we do not
1278 // want to return true because mFrame won't move relative to its viewport.
1279 nsIFrame* rootMover = aBuilder->GetRootMovingFrame();
1280 return mFrame == rootMover ||
1281 nsLayoutUtils::IsProperAncestorFrame(rootMover, mFrame);
1284 /* static */ void
1285 nsDisplayTableItem::UpdateForFrameBackground(nsIFrame* aFrame)
1287 PRBool isCanvas;
1288 const nsStyleBackground* bg;
1289 PRBool hasBG =
1290 nsCSSRendering::FindBackground(aFrame->PresContext(), aFrame, &bg, &isCanvas);
1291 if (!hasBG)
1292 return;
1293 if (!bg->HasFixedBackground())
1294 return;
1296 mPartHasFixedBackground = PR_TRUE;
1299 class nsDisplayTableBorderBackground : public nsDisplayTableItem {
1300 public:
1301 nsDisplayTableBorderBackground(nsTableFrame* aFrame) : nsDisplayTableItem(aFrame) {
1302 MOZ_COUNT_CTOR(nsDisplayTableBorderBackground);
1304 #ifdef NS_BUILD_REFCNT_LOGGING
1305 virtual ~nsDisplayTableBorderBackground() {
1306 MOZ_COUNT_DTOR(nsDisplayTableBorderBackground);
1308 #endif
1310 virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
1311 const nsRect& aDirtyRect);
1312 NS_DISPLAY_DECL_NAME("TableBorderBackground")
1315 void
1316 nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
1317 nsIRenderingContext* aCtx, const nsRect& aDirtyRect)
1319 static_cast<nsTableFrame*>(mFrame)->
1320 PaintTableBorderBackground(*aCtx, aDirtyRect,
1321 aBuilder->ToReferenceFrame(mFrame));
1324 static PRInt32 GetTablePartRank(nsDisplayItem* aItem)
1326 nsIAtom* type = aItem->GetUnderlyingFrame()->GetType();
1327 if (type == nsGkAtoms::tableFrame)
1328 return 0;
1329 if (type == nsGkAtoms::tableRowGroupFrame)
1330 return 1;
1331 if (type == nsGkAtoms::tableRowFrame)
1332 return 2;
1333 return 3;
1336 static PRBool CompareByTablePartRank(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
1337 void* aClosure)
1339 return GetTablePartRank(aItem1) <= GetTablePartRank(aItem2);
1342 /* static */ nsresult
1343 nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
1344 const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
1346 // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren
1347 // does, except that we allow the children's background and borders to go
1348 // in our BorderBackground list. This doesn't really affect background
1349 // painting --- the children won't actually draw their own backgrounds
1350 // because the nsTableFrame already drew them, unless a child has its own
1351 // stacking context, in which case the child won't use its passed-in
1352 // BorderBackground list anyway. It does affect cell borders though; this
1353 // lets us get cell borders into the nsTableFrame's BorderBackground list.
1354 nsIFrame* kid = aFrame->GetFirstChild(nsnull);
1355 while (kid) {
1356 nsresult rv = aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
1357 NS_ENSURE_SUCCESS(rv, rv);
1358 kid = kid->GetNextSibling();
1360 return NS_OK;
1363 /* static */ nsresult
1364 nsTableFrame::DisplayGenericTablePart(nsDisplayListBuilder* aBuilder,
1365 nsFrame* aFrame,
1366 const nsRect& aDirtyRect,
1367 const nsDisplayListSet& aLists,
1368 nsDisplayTableItem* aDisplayItem,
1369 DisplayGenericTablePartTraversal aTraversal)
1371 nsDisplayList eventsBorderBackground;
1372 // If we need to sort the event backgrounds, then we'll put descendants'
1373 // display items into their own set of lists.
1374 PRBool sortEventBackgrounds = aDisplayItem && aBuilder->IsForEventDelivery();
1375 nsDisplayListCollection separatedCollection;
1376 const nsDisplayListSet* lists = sortEventBackgrounds ? &separatedCollection : &aLists;
1378 nsAutoPushCurrentTableItem pushTableItem;
1379 if (aDisplayItem) {
1380 pushTableItem.Push(aBuilder, aDisplayItem);
1382 nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
1383 NS_ASSERTION(currentItem, "No current table item!");
1384 currentItem->UpdateForFrameBackground(aFrame);
1386 // Paint the box-shadow for the table frames
1387 if (aFrame->IsVisibleForPainting(aBuilder) &&
1388 aFrame->GetStyleBorder()->mBoxShadow) {
1389 nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadow(aFrame);
1390 nsresult rv = lists->BorderBackground()->AppendNewToTop(item);
1391 NS_ENSURE_SUCCESS(rv, rv);
1394 // Create dedicated background display items per-frame when we're
1395 // handling events.
1396 // XXX how to handle collapsed borders?
1397 if (aBuilder->IsForEventDelivery() &&
1398 aFrame->IsVisibleForPainting(aBuilder)) {
1399 nsresult rv = lists->BorderBackground()->AppendNewToTop(new (aBuilder)
1400 nsDisplayBackground(aFrame));
1401 NS_ENSURE_SUCCESS(rv, rv);
1404 nsresult rv = aTraversal(aBuilder, aFrame, aDirtyRect, *lists);
1405 NS_ENSURE_SUCCESS(rv, rv);
1407 if (sortEventBackgrounds) {
1408 // Ensure that the table frame event background goes before the
1409 // table rowgroups event backgrounds, before the table row event backgrounds,
1410 // before everything else (cells and their blocks)
1411 separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nsnull);
1412 separatedCollection.MoveTo(aLists);
1415 return aFrame->DisplayOutline(aBuilder, aLists);
1418 // table paint code is concerned primarily with borders and bg color
1419 // SEC: TODO: adjust the rect for captions
1420 NS_IMETHODIMP
1421 nsTableFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1422 const nsRect& aDirtyRect,
1423 const nsDisplayListSet& aLists)
1425 if (!IsVisibleInSelection(aBuilder))
1426 return NS_OK;
1428 DO_GLOBAL_REFLOW_COUNT_DSP_COLOR("nsTableFrame", NS_RGB(255,128,255));
1430 // This background is created regardless of whether this frame is
1431 // visible or not. Visibility decisions are delegated to the
1432 // table background painter.
1433 nsDisplayTableItem* item = new (aBuilder) nsDisplayTableBorderBackground(this);
1434 nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
1435 NS_ENSURE_SUCCESS(rv, rv);
1437 return DisplayGenericTablePart(aBuilder, this, aDirtyRect, aLists, item);
1440 // XXX We don't put the borders and backgrounds in tree order like we should.
1441 // That requires some major surgery which we aren't going to do right now.
1442 void
1443 nsTableFrame::PaintTableBorderBackground(nsIRenderingContext& aRenderingContext,
1444 const nsRect& aDirtyRect,
1445 nsPoint aPt)
1447 nsPresContext* presContext = PresContext();
1449 TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table,
1450 presContext, aRenderingContext,
1451 aDirtyRect, aPt);
1452 nsresult rv;
1454 if (eCompatibility_NavQuirks == presContext->CompatibilityMode()) {
1455 nsMargin deflate(0,0,0,0);
1456 if (IsBorderCollapse()) {
1457 PRInt32 p2t = nsPresContext::AppUnitsPerCSSPixel();
1458 BCPropertyData* propData =
1459 (BCPropertyData*)nsTableFrame::GetProperty((nsIFrame*)this,
1460 nsGkAtoms::tableBCProperty,
1461 PR_FALSE);
1462 if (propData) {
1463 deflate.top = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
1464 deflate.right = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
1465 deflate.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
1466 deflate.left = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
1469 rv = painter.PaintTable(this, &deflate);
1470 if (NS_FAILED(rv)) return;
1472 else {
1473 rv = painter.PaintTable(this, nsnull);
1474 if (NS_FAILED(rv)) return;
1477 if (GetStyleVisibility()->IsVisible()) {
1478 const nsStyleBorder* border = GetStyleBorder();
1479 if (!IsBorderCollapse()) {
1480 PRIntn skipSides = GetSkipSides();
1481 nsRect rect(aPt, mRect.Size());
1482 nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
1483 aDirtyRect, rect, *border, mStyleContext,
1484 skipSides);
1486 else {
1487 // XXX we should probably get rid of this translation at some stage
1488 // But that would mean modifying PaintBCBorders, ugh
1489 nsIRenderingContext::AutoPushTranslation translate(&aRenderingContext, aPt.x, aPt.y);
1490 PaintBCBorders(aRenderingContext, aDirtyRect - aPt);
1495 //null range means the whole thing
1496 NS_IMETHODIMP
1497 nsTableFrame::SetSelected(nsPresContext* aPresContext,
1498 nsIDOMRange *aRange,
1499 PRBool aSelected,
1500 nsSpread aSpread,
1501 SelectionType aType)
1503 #if 0
1504 //traverse through children unselect tables
1505 if ((aSpread == eSpreadDown)){
1506 nsIFrame* kid = GetFirstChild(nsnull);
1507 while (kid) {
1508 kid->SetSelected(nsnull, aSelected, eSpreadDown);
1509 kid = kid->GetNextSibling();
1512 #endif
1513 // Must call base class to set mSelected state and trigger repaint of frame
1514 // Note that in current version, aRange and aSpread are ignored,
1515 // only this frame is considered
1516 nsFrame::SetSelected(aPresContext, aRange, aSelected, aSpread, aType);
1517 return NS_OK;//return nsFrame::SetSelected(aRange,aSelected,eSpreadNone, aType);
1521 PRBool nsTableFrame::ParentDisablesSelection() const //override default behavior
1523 PRBool returnval;
1524 if (NS_FAILED(GetSelected(&returnval)))
1525 return PR_FALSE;
1526 if (returnval)
1527 return PR_TRUE;
1528 return nsFrame::ParentDisablesSelection();
1531 PRIntn
1532 nsTableFrame::GetSkipSides() const
1534 PRIntn skip = 0;
1535 // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
1536 // account for pagination
1537 if (nsnull != GetPrevInFlow()) {
1538 skip |= 1 << NS_SIDE_TOP;
1540 if (nsnull != GetNextInFlow()) {
1541 skip |= 1 << NS_SIDE_BOTTOM;
1543 return skip;
1546 void
1547 nsTableFrame::SetColumnDimensions(nscoord aHeight,
1548 const nsMargin& aBorderPadding)
1550 nscoord cellSpacingX = GetCellSpacingX();
1551 nscoord cellSpacingY = GetCellSpacingY();
1552 nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom +
1553 2* cellSpacingY;
1555 nsTableIterator iter(mColGroups);
1556 nsIFrame* colGroupFrame = iter.First();
1557 PRBool tableIsLTR = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
1558 PRInt32 colX =tableIsLTR ? 0 : PR_MAX(0, GetColCount() - 1);
1559 PRInt32 tableColIncr = tableIsLTR ? 1 : -1;
1560 nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX,
1561 aBorderPadding.top + cellSpacingY);
1562 while (nsnull != colGroupFrame) {
1563 nscoord colGroupWidth = 0;
1564 nsTableIterator iterCol(*colGroupFrame);
1565 nsIFrame* colFrame = iterCol.First();
1566 nsPoint colOrigin(0,0);
1567 while (nsnull != colFrame) {
1568 if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
1569 colFrame->GetStyleDisplay()->mDisplay) {
1570 NS_ASSERTION(colX < GetColCount(), "invalid number of columns");
1571 nscoord colWidth = GetColumnWidth(colX);
1572 nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight);
1573 colFrame->SetRect(colRect);
1574 colOrigin.x += colWidth + cellSpacingX;
1575 colGroupWidth += colWidth + cellSpacingX;
1576 colX += tableColIncr;
1578 colFrame = iterCol.Next();
1580 if (colGroupWidth) {
1581 colGroupWidth -= cellSpacingX;
1584 nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight);
1585 colGroupFrame->SetRect(colGroupRect);
1586 colGroupFrame = iter.Next();
1587 colGroupOrigin.x += colGroupWidth + cellSpacingX;
1591 // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
1593 // XXX this could be made more general to handle row modifications that change the
1594 // table height, but first we need to scrutinize every Invalidate
1595 void
1596 nsTableFrame::ProcessRowInserted(nscoord aNewHeight)
1598 SetRowInserted(PR_FALSE); // reset the bit that got us here
1599 nsTableFrame::RowGroupArray rowGroups;
1600 OrderRowGroups(rowGroups);
1601 // find the row group containing the inserted row
1602 for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
1603 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
1604 NS_ASSERTION(rgFrame, "Must have rgFrame here");
1605 nsIFrame* childFrame = rgFrame->GetFirstChild(nsnull);
1606 // find the row that was inserted first
1607 while (childFrame) {
1608 if (nsGkAtoms::tableRowFrame == childFrame->GetType()) {
1609 nsTableRowFrame* rowFrame = (nsTableRowFrame*)childFrame;
1610 if (rowFrame->IsFirstInserted()) {
1611 rowFrame->SetFirstInserted(PR_FALSE);
1612 // damage the table from the 1st row inserted to the end of the table
1613 nscoord damageY = rgFrame->GetPosition().y + rowFrame->GetPosition().y;
1614 nsRect damageRect(0, damageY, GetSize().width, aNewHeight - damageY);
1616 Invalidate(damageRect);
1617 // XXXbz didn't we do this up front? Why do we need to do it again?
1618 SetRowInserted(PR_FALSE);
1619 return; // found it, so leave
1622 childFrame = childFrame->GetNextSibling();
1627 /* virtual */ void
1628 nsTableFrame::MarkIntrinsicWidthsDirty()
1630 LayoutStrategy()->MarkIntrinsicWidthsDirty();
1632 // XXXldb Call SetBCDamageArea?
1634 nsHTMLContainerFrame::MarkIntrinsicWidthsDirty();
1637 /* virtual */ nscoord
1638 nsTableFrame::GetMinWidth(nsIRenderingContext *aRenderingContext)
1640 if (NeedToCalcBCBorders())
1641 CalcBCBorders();
1643 ReflowColGroups(aRenderingContext);
1645 return LayoutStrategy()->GetMinWidth(aRenderingContext);
1648 /* virtual */ nscoord
1649 nsTableFrame::GetPrefWidth(nsIRenderingContext *aRenderingContext)
1651 if (NeedToCalcBCBorders())
1652 CalcBCBorders();
1654 ReflowColGroups(aRenderingContext);
1656 return LayoutStrategy()->GetPrefWidth(aRenderingContext, PR_FALSE);
1659 /* virtual */ nsIFrame::IntrinsicWidthOffsetData
1660 nsTableFrame::IntrinsicWidthOffsets(nsIRenderingContext* aRenderingContext)
1662 IntrinsicWidthOffsetData result =
1663 nsHTMLContainerFrame::IntrinsicWidthOffsets(aRenderingContext);
1665 if (IsBorderCollapse()) {
1666 result.hPadding = 0;
1667 result.hPctPadding = 0;
1669 nsMargin outerBC = GetIncludedOuterBCBorder();
1670 result.hBorder = outerBC.LeftRight();
1673 return result;
1676 /* virtual */ nsSize
1677 nsTableFrame::ComputeSize(nsIRenderingContext *aRenderingContext,
1678 nsSize aCBSize, nscoord aAvailableWidth,
1679 nsSize aMargin, nsSize aBorder, nsSize aPadding,
1680 PRBool aShrinkWrap)
1682 nsSize result =
1683 nsHTMLContainerFrame::ComputeSize(aRenderingContext, aCBSize,
1684 aAvailableWidth,
1685 aMargin, aBorder, aPadding, aShrinkWrap);
1687 // Tables never shrink below their min width.
1688 nscoord minWidth = GetMinWidth(aRenderingContext);
1689 if (minWidth > result.width)
1690 result.width = minWidth;
1692 return result;
1695 nscoord
1696 nsTableFrame::TableShrinkWidthToFit(nsIRenderingContext *aRenderingContext,
1697 nscoord aWidthInCB)
1699 nscoord result;
1700 nscoord minWidth = GetMinWidth(aRenderingContext);
1701 if (minWidth > aWidthInCB) {
1702 result = minWidth;
1703 } else {
1704 // Tables shrink width to fit with a slightly different algorithm
1705 // from the one they use for their intrinsic widths (the difference
1706 // relates to handling of percentage widths on columns). So this
1707 // function differs from nsFrame::ShrinkWidthToFit by only the
1708 // following line.
1709 // Since we've already called GetMinWidth, we don't need to do any
1710 // of the other stuff GetPrefWidth does.
1711 nscoord prefWidth =
1712 LayoutStrategy()->GetPrefWidth(aRenderingContext, PR_TRUE);
1713 if (prefWidth > aWidthInCB) {
1714 result = aWidthInCB;
1715 } else {
1716 result = prefWidth;
1719 return result;
1722 /* virtual */ nsSize
1723 nsTableFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
1724 nsSize aCBSize, nscoord aAvailableWidth,
1725 nsSize aMargin, nsSize aBorder, nsSize aPadding,
1726 PRBool aShrinkWrap)
1728 // Tables always shrink-wrap.
1729 nscoord cbBased = aAvailableWidth - aMargin.width - aBorder.width -
1730 aPadding.width;
1731 return nsSize(TableShrinkWidthToFit(aRenderingContext, cbBased),
1732 NS_UNCONSTRAINEDSIZE);
1735 // Return true if aParentReflowState.frame or any of its ancestors within
1736 // the containing table have non-auto height. (e.g. pct or fixed height)
1737 PRBool
1738 nsTableFrame::AncestorsHaveStyleHeight(const nsHTMLReflowState& aParentReflowState)
1740 for (const nsHTMLReflowState* rs = &aParentReflowState;
1741 rs && rs->frame; rs = rs->parentReflowState) {
1742 nsIAtom* frameType = rs->frame->GetType();
1743 if (IS_TABLE_CELL(frameType) ||
1744 (nsGkAtoms::tableRowFrame == frameType) ||
1745 (nsGkAtoms::tableRowGroupFrame == frameType)) {
1746 if (rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) {
1747 return PR_TRUE;
1750 else if (nsGkAtoms::tableFrame == frameType) {
1751 // we reached the containing table, so always return
1752 if (rs->mStylePosition->mHeight.GetUnit() != eStyleUnit_Auto) {
1753 return PR_TRUE;
1755 else return PR_FALSE;
1758 return PR_FALSE;
1761 // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow
1762 void
1763 nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
1765 if (!aReflowState.frame->GetPrevInFlow() && // 1st in flow
1766 (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight() || // no computed height
1767 0 == aReflowState.ComputedHeight()) &&
1768 eStyleUnit_Percent == aReflowState.mStylePosition->mHeight.GetUnit() && // pct height
1769 nsTableFrame::AncestorsHaveStyleHeight(*aReflowState.parentReflowState)) {
1770 nsTableFrame::RequestSpecialHeightReflow(aReflowState);
1774 // Notify the frame and its ancestors (up to the containing table) that a special
1775 // height reflow will occur. During a special height reflow, a table, row group,
1776 // row, or cell returns the last size it was reflowed at. However, the table may
1777 // change the height of row groups, rows, cells in DistributeHeightToRows after.
1778 // And the row group can change the height of rows, cells in CalculateRowHeights.
1779 void
1780 nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
1782 // notify the frame and its ancestors of the special reflow, stopping at the containing table
1783 for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) {
1784 nsIAtom* frameType = rs->frame->GetType();
1785 NS_ASSERTION(IS_TABLE_CELL(frameType) ||
1786 nsGkAtoms::tableRowFrame == frameType ||
1787 nsGkAtoms::tableRowGroupFrame == frameType ||
1788 nsGkAtoms::scrollFrame == frameType ||
1789 nsGkAtoms::tableFrame == frameType,
1790 "unexpected frame type");
1792 rs->frame->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
1793 if (nsGkAtoms::tableFrame == frameType) {
1794 NS_ASSERTION(rs != &aReflowState,
1795 "should not request special height reflow for table");
1796 // always stop when we reach a table
1797 break;
1802 /******************************************************************************************
1803 * Before reflow, intrinsic width calculation is done using GetMinWidth
1804 * and GetPrefWidth. This used to be known as pass 1 reflow.
1806 * After the intrinsic width calculation, the table determines the
1807 * column widths using BalanceColumnWidths() and
1808 * then reflows each child again with a constrained avail width. This reflow is referred to
1809 * as the pass 2 reflow.
1811 * A special height reflow (pass 3 reflow) can occur during an initial or resize reflow
1812 * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed
1813 * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables
1814 * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is
1815 * necessary because the table cannot split until after the pass 2 reflow. The mechanics of
1816 * the special height reflow (variety a) are as follows:
1818 * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
1819 * to indicate that it should get the reflow. It does this when it has a percent height but
1820 * no computed height by calling CheckRequestSpecialHeightReflow(). This method calls
1821 * RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until
1822 * it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For
1823 * percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight()
1824 * is called (the cell is the reflow state's mPercentHeightObserver in this case).
1825 * NotifyPercentHeight() calls RequestSpecialHeightReflow().
1827 * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
1828 * will do the special height reflow, setting the reflow state's mFlags.mSpecialHeightReflow
1829 * to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
1830 * returns true because in that case another special height reflow will be coming along with the
1831 * containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
1832 * the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
1833 * appropriate heights will not be known.
1835 * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2
1836 * reflow, they return their last desired sizes during the special height reflow. The reflow only
1837 * permits percent height frames inside the cells to resize based on the cells height and that height
1838 * was determined during the pass 2 reflow.
1840 * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
1841 * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
1842 * until the current initiator is its containing table. Since these reflows are only received by
1843 * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
1845 * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
1846 * that all frames will receive the reflow even if they don't really need them.
1848 * Open issues with the special height reflow:
1850 * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are
1851 * really quite different. This would avoid unnecessary reflows during printing.
1852 * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245).
1853 * However, this can also occur if a cell has a fixed height and there is no special height reflow.
1855 * XXXldb Special height reflow should really be its own method, not
1856 * part of nsIFrame::Reflow. It should then call nsIFrame::Reflow on
1857 * the contents of the cells to do the necessary vertical resizing.
1859 ******************************************************************************************/
1861 /* Layout the entire inner table. */
1862 NS_METHOD nsTableFrame::Reflow(nsPresContext* aPresContext,
1863 nsHTMLReflowMetrics& aDesiredSize,
1864 const nsHTMLReflowState& aReflowState,
1865 nsReflowStatus& aStatus)
1867 DO_GLOBAL_REFLOW_COUNT("nsTableFrame");
1868 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1869 PRBool isPaginated = aPresContext->IsPaginated();
1871 aStatus = NS_FRAME_COMPLETE;
1872 if (!GetPrevInFlow() && !mTableLayoutStrategy) {
1873 NS_ASSERTION(PR_FALSE, "strategy should have been created in Init");
1874 return NS_ERROR_NULL_POINTER;
1876 nsresult rv = NS_OK;
1878 // see if collapsing borders need to be calculated
1879 if (!GetPrevInFlow() && IsBorderCollapse() && NeedToCalcBCBorders()) {
1880 CalcBCBorders();
1883 aDesiredSize.width = aReflowState.availableWidth;
1885 // Check for an overflow list, and append any row group frames being pushed
1886 MoveOverflowToChildList(aPresContext);
1888 PRBool haveDesiredHeight = PR_FALSE;
1889 SetHaveReflowedColGroups(PR_FALSE);
1891 // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a
1892 // constrained initial reflow and other reflows which require either a strategy init or balance.
1893 // This isn't done during an unconstrained reflow, because it will occur later when the parent
1894 // reflows with a constrained width.
1895 if (NS_SUBTREE_DIRTY(this) ||
1896 aReflowState.ShouldReflowAllKids() ||
1897 IsGeometryDirty() ||
1898 aReflowState.mFlags.mVResize) {
1900 if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE ||
1901 // Also check mVResize, to handle the first Reflow preceding a
1902 // special height Reflow, when we've already had a special height
1903 // Reflow (where mComputedHeight would not be
1904 // NS_UNCONSTRAINEDSIZE, but without a style change in between).
1905 aReflowState.mFlags.mVResize) {
1906 // XXX Eventually, we should modify DistributeHeightToRows to use
1907 // nsTableRowFrame::GetHeight instead of nsIFrame::GetSize().height.
1908 // That way, it will make its calculations based on internal table
1909 // frame heights as they are before they ever had any extra height
1910 // distributed to them. In the meantime, this reflows all the
1911 // internal table frames, which restores them to their state before
1912 // DistributeHeightToRows was called.
1913 SetGeometryDirty();
1916 PRBool needToInitiateSpecialReflow =
1917 !!(GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT);
1918 // see if an extra reflow will be necessary in pagination mode when there is a specified table height
1919 if (isPaginated && !GetPrevInFlow() && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) {
1920 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
1921 if ((tableSpecifiedHeight > 0) &&
1922 (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) {
1923 needToInitiateSpecialReflow = PR_TRUE;
1926 nsIFrame* lastChildReflowed = nsnull;
1928 NS_ASSERTION(!aReflowState.mFlags.mSpecialHeightReflow,
1929 "Shouldn't be in special height reflow here!");
1931 // do the pass 2 reflow unless this is a special height reflow and we will be
1932 // initiating a special height reflow
1933 // XXXldb I changed this. Should I change it back?
1935 // if we need to initiate a special height reflow, then don't constrain the
1936 // height of the reflow before that
1937 nscoord availHeight = needToInitiateSpecialReflow
1938 ? NS_UNCONSTRAINEDSIZE : aReflowState.availableHeight;
1940 ReflowTable(aDesiredSize, aReflowState, availHeight,
1941 lastChildReflowed, aStatus);
1943 // reevaluate special height reflow conditions
1944 if (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)
1945 needToInitiateSpecialReflow = PR_TRUE;
1947 // XXXldb Are all these conditions correct?
1948 if (needToInitiateSpecialReflow && NS_FRAME_IS_COMPLETE(aStatus)) {
1949 // XXXldb Do we need to set the mVResize flag on any reflow states?
1951 nsHTMLReflowState &mutable_rs =
1952 const_cast<nsHTMLReflowState&>(aReflowState);
1954 // distribute extra vertical space to rows
1955 CalcDesiredHeight(aReflowState, aDesiredSize);
1956 mutable_rs.mFlags.mSpecialHeightReflow = PR_TRUE;
1958 ReflowTable(aDesiredSize, aReflowState, aReflowState.availableHeight,
1959 lastChildReflowed, aStatus);
1961 if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
1962 // if there is an incomplete child, then set the desired height to include it but not the next one
1963 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
1964 aDesiredSize.height = borderPadding.bottom + GetCellSpacingY() +
1965 lastChildReflowed->GetRect().YMost();
1967 haveDesiredHeight = PR_TRUE;
1969 mutable_rs.mFlags.mSpecialHeightReflow = PR_FALSE;
1972 else {
1973 // Calculate the overflow area contribution from our children.
1974 for (nsIFrame* kid = GetFirstChild(nsnull); kid; kid = kid->GetNextSibling()) {
1975 ConsiderChildOverflow(aDesiredSize.mOverflowArea, kid);
1979 aDesiredSize.width = aReflowState.ComputedWidth() +
1980 aReflowState.mComputedBorderPadding.LeftRight();
1981 if (!haveDesiredHeight) {
1982 CalcDesiredHeight(aReflowState, aDesiredSize);
1984 if (IsRowInserted()) {
1985 ProcessRowInserted(aDesiredSize.height);
1988 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
1989 SetColumnDimensions(aDesiredSize.height, borderPadding);
1990 if (NeedToCollapse() &&
1991 (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth)) {
1992 AdjustForCollapsingRowsCols(aDesiredSize, borderPadding);
1995 // make sure the table overflow area does include the table rect.
1996 nsRect tableRect(0, 0, aDesiredSize.width, aDesiredSize.height) ;
1998 if (!aReflowState.mStyleDisplay->IsTableClip()) {
1999 // collapsed border may leak out
2000 nsMargin bcMargin = GetExcludedOuterBCBorder();
2001 tableRect.Inflate(bcMargin);
2003 aDesiredSize.mOverflowArea.UnionRect(aDesiredSize.mOverflowArea, tableRect);
2005 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) {
2006 // Fulfill the promise InvalidateFrame makes.
2007 Invalidate(aDesiredSize.mOverflowArea);
2008 } else {
2009 CheckInvalidateSizeChange(aDesiredSize);
2012 FinishAndStoreOverflow(&aDesiredSize);
2013 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
2014 return rv;
2017 nsresult
2018 nsTableFrame::ReflowTable(nsHTMLReflowMetrics& aDesiredSize,
2019 const nsHTMLReflowState& aReflowState,
2020 nscoord aAvailHeight,
2021 nsIFrame*& aLastChildReflowed,
2022 nsReflowStatus& aStatus)
2024 nsresult rv = NS_OK;
2025 aLastChildReflowed = nsnull;
2027 if (!GetPrevInFlow()) {
2028 mTableLayoutStrategy->ComputeColumnWidths(aReflowState);
2030 // Constrain our reflow width to the computed table width (of the 1st in flow).
2031 // and our reflow height to our avail height minus border, padding, cellspacing
2032 aDesiredSize.width = aReflowState.ComputedWidth() +
2033 aReflowState.mComputedBorderPadding.LeftRight();
2034 nsTableReflowState reflowState(*PresContext(), aReflowState, *this,
2035 aDesiredSize.width, aAvailHeight);
2036 ReflowChildren(reflowState, aStatus, aLastChildReflowed,
2037 aDesiredSize.mOverflowArea);
2039 ReflowColGroups(aReflowState.rendContext);
2040 return rv;
2043 nsIFrame*
2044 nsTableFrame::GetFirstBodyRowGroupFrame()
2046 nsIFrame* headerFrame = nsnull;
2047 nsIFrame* footerFrame = nsnull;
2049 for (nsIFrame* kidFrame = mFrames.FirstChild(); nsnull != kidFrame; ) {
2050 const nsStyleDisplay* childDisplay = kidFrame->GetStyleDisplay();
2052 // We expect the header and footer row group frames to be first, and we only
2053 // allow one header and one footer
2054 if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) {
2055 if (headerFrame) {
2056 // We already have a header frame and so this header frame is treated
2057 // like an ordinary body row group frame
2058 return kidFrame;
2060 headerFrame = kidFrame;
2062 } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) {
2063 if (footerFrame) {
2064 // We already have a footer frame and so this footer frame is treated
2065 // like an ordinary body row group frame
2066 return kidFrame;
2068 footerFrame = kidFrame;
2070 } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) {
2071 return kidFrame;
2074 // Get the next child
2075 kidFrame = kidFrame->GetNextSibling();
2078 return nsnull;
2081 // Table specific version that takes into account repeated header and footer
2082 // frames when continuing table frames
2083 void
2084 nsTableFrame::PushChildren(const FrameArray& aFrames,
2085 PRInt32 aPushFrom)
2087 NS_PRECONDITION(aPushFrom > 0, "pushing first child");
2089 // extract the frames from the array into a sibling list
2090 nsFrameList frames;
2091 nsIFrame* lastFrame = nsnull;
2092 PRUint32 childX;
2093 nsIFrame* prevSiblingHint = aFrames.SafeElementAt(aPushFrom - 1);
2094 for (childX = aPushFrom; childX < aFrames.Length(); ++childX) {
2095 nsIFrame* f = aFrames[childX];
2096 // Don't push repeatable frames, do push non-rowgroup frames.
2097 // XXXbz Need to push the non-rowgroup frames, even though we don't reflow
2098 // them, so that we don't lose them. Of course there shouldn't be any
2099 // non-rowgroup frames here...
2100 nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(f);
2101 NS_ASSERTION(rgFrame, "Unexpected non-row-group frame");
2102 if (!rgFrame || !rgFrame->IsRepeatable()) {
2103 mFrames.RemoveFrame(f, prevSiblingHint);
2104 frames.InsertFrame(nsnull, lastFrame, f);
2105 lastFrame = f;
2109 if (nsnull != GetNextInFlow()) {
2110 nsTableFrame* nextInFlow = (nsTableFrame*)GetNextInFlow();
2112 // Insert the frames after any repeated header and footer frames
2113 nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
2114 nsIFrame* prevSibling = nsnull;
2115 if (firstBodyFrame) {
2116 prevSibling = nextInFlow->mFrames.GetPrevSiblingFor(firstBodyFrame);
2118 // When pushing and pulling frames we need to check for whether any
2119 // views need to be reparented.
2120 for (nsIFrame* f = frames.FirstChild(); f; f = f->GetNextSibling()) {
2121 nsHTMLContainerFrame::ReparentFrameView(PresContext(), f, this, nextInFlow);
2123 nextInFlow->mFrames.InsertFrames(GetNextInFlow(), prevSibling, frames.FirstChild());
2125 else {
2126 // Add the frames to our overflow list
2127 SetOverflowFrames(PresContext(), frames.FirstChild());
2131 // Table specific version that takes into account header and footer row group
2132 // frames that are repeated for continuing table frames
2134 // Appends the overflow frames to the end of the child list, just like the
2135 // nsContainerFrame version does, except that there are no assertions that
2136 // the child list is empty (it may not be empty, because there may be repeated
2137 // header/footer frames)
2138 PRBool
2139 nsTableFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
2141 PRBool result = PR_FALSE;
2143 // Check for an overflow list with our prev-in-flow
2144 nsTableFrame* prevInFlow = (nsTableFrame*)GetPrevInFlow();
2145 if (prevInFlow) {
2146 nsIFrame* prevOverflowFrames = prevInFlow->GetOverflowFrames(aPresContext, PR_TRUE);
2147 if (prevOverflowFrames) {
2148 // When pushing and pulling frames we need to check for whether any
2149 // views need to be reparented.
2150 for (nsIFrame* f = prevOverflowFrames; f; f = f->GetNextSibling()) {
2151 nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevInFlow, this);
2153 mFrames.AppendFrames(this, prevOverflowFrames);
2154 result = PR_TRUE;
2158 // It's also possible that we have an overflow list for ourselves
2159 nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
2160 if (overflowFrames) {
2161 mFrames.AppendFrames(nsnull, overflowFrames);
2162 result = PR_TRUE;
2164 return result;
2169 // collapsing row groups, rows, col groups and cols are accounted for after both passes of
2170 // reflow so that it has no effect on the calculations of reflow.
2171 void
2172 nsTableFrame::AdjustForCollapsingRowsCols(nsHTMLReflowMetrics& aDesiredSize,
2173 nsMargin aBorderPadding)
2175 nscoord yTotalOffset = 0; // total offset among all rows in all row groups
2177 // reset the bit, it will be set again if row/rowgroup is collapsed
2178 SetNeedToCollapse(PR_FALSE);
2180 // collapse the rows and/or row groups as necessary
2181 // Get the ordered children
2182 RowGroupArray rowGroups;
2183 OrderRowGroups(rowGroups);
2184 nscoord width = GetCollapsedWidth(aBorderPadding);
2185 nscoord rgWidth = width - 2 * GetCellSpacingX();
2186 nsRect overflowArea(0, 0, 0, 0);
2187 // Walk the list of children
2188 for (PRUint32 childX = 0; childX < rowGroups.Length(); childX++) {
2189 nsTableRowGroupFrame* rgFrame = rowGroups[childX];
2190 NS_ASSERTION(rgFrame, "Must have row group frame here");
2191 yTotalOffset += rgFrame->CollapseRowGroupIfNecessary(yTotalOffset, rgWidth);
2192 ConsiderChildOverflow(overflowArea, rgFrame);
2195 aDesiredSize.height -= yTotalOffset;
2196 aDesiredSize.width = width;
2197 overflowArea.UnionRect(nsRect(0, 0, aDesiredSize.width, aDesiredSize.height),
2198 overflowArea);
2199 FinishAndStoreOverflow(&overflowArea,
2200 nsSize(aDesiredSize.width, aDesiredSize.height));
2203 nscoord
2204 nsTableFrame::GetCollapsedWidth(nsMargin aBorderPadding)
2206 nscoord cellSpacingX = GetCellSpacingX();
2207 nscoord width = cellSpacingX;
2208 width += aBorderPadding.left + aBorderPadding.right;
2209 for (nsIFrame* groupFrame = mColGroups.FirstChild(); groupFrame;
2210 groupFrame = groupFrame->GetNextSibling()) {
2211 const nsStyleVisibility* groupVis = groupFrame->GetStyleVisibility();
2212 PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
2213 nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)groupFrame;
2214 for (nsTableColFrame* colFrame = cgFrame->GetFirstColumn(); colFrame;
2215 colFrame = colFrame->GetNextCol()) {
2216 const nsStyleDisplay* colDisplay = colFrame->GetStyleDisplay();
2217 PRInt32 colX = colFrame->GetColIndex();
2218 if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) {
2219 const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
2220 PRBool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
2221 PRInt32 colWidth = GetColumnWidth(colX);
2222 if (!collapseGroup && !collapseCol) {
2223 width += colWidth;
2224 if (ColumnHasCellSpacingBefore(colX))
2225 width += cellSpacingX;
2230 return width;
2233 /* virtual */ void
2234 nsTableFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
2236 if (!aOldStyleContext) //avoid this on init
2237 return;
2239 if (IsBorderCollapse() &&
2240 BCRecalcNeeded(aOldStyleContext, GetStyleContext())) {
2241 nsRect damageArea(0, 0, GetColCount(), GetRowCount());
2242 SetBCDamageArea(damageArea);
2245 //avoid this on init or nextinflow
2246 if (!mTableLayoutStrategy || GetPrevInFlow())
2247 return;
2249 PRBool isAuto = IsAutoLayout();
2250 if (isAuto != (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Auto)) {
2251 nsITableLayoutStrategy* temp;
2252 if (isAuto)
2253 temp = new BasicTableLayoutStrategy(this);
2254 else
2255 temp = new FixedTableLayoutStrategy(this);
2257 if (temp) {
2258 delete mTableLayoutStrategy;
2259 mTableLayoutStrategy = temp;
2266 NS_IMETHODIMP
2267 nsTableFrame::AppendFrames(nsIAtom* aListName,
2268 nsIFrame* aFrameList)
2270 NS_ASSERTION(!aListName || aListName == nsGkAtoms::colGroupList,
2271 "unexpected child list");
2273 // Because we actually have two child lists, one for col group frames and one
2274 // for everything else, we need to look at each frame individually
2275 // XXX The frame construction code should be separating out child frames
2276 // based on the type, bug 343048.
2277 nsIFrame* f = aFrameList;
2278 while (f) {
2279 // Get the next frame and disconnect this frame from its sibling
2280 nsIFrame* next = f->GetNextSibling();
2281 f->SetNextSibling(nsnull);
2283 // See what kind of frame we have
2284 const nsStyleDisplay* display = f->GetStyleDisplay();
2286 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
2287 nsTableColGroupFrame* lastColGroup;
2288 PRBool doAppend = nsTableColGroupFrame::GetLastRealColGroup(this, (nsIFrame**) &lastColGroup);
2289 PRInt32 startColIndex = (lastColGroup)
2290 ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
2291 if (doAppend) {
2292 // Append the new col group frame
2293 mColGroups.AppendFrame(nsnull, f);
2295 else {
2296 // there is a colgroup after the last real one
2297 mColGroups.InsertFrame(nsnull, lastColGroup, f);
2299 // Insert the colgroup and its cols into the table
2300 InsertColGroups(startColIndex, f, f);
2301 } else if (IsRowGroup(display->mDisplay)) {
2302 // Append the new row group frame to the sibling chain
2303 mFrames.AppendFrame(nsnull, f);
2305 // insert the row group and its rows into the table
2306 InsertRowGroups(f, f);
2307 } else {
2308 // Nothing special to do, just add the frame to our child list
2309 mFrames.AppendFrame(nsnull, f);
2312 // Move to the next frame
2313 f = next;
2316 #ifdef DEBUG_TABLE_CELLMAP
2317 printf("=== TableFrame::AppendFrames\n");
2318 Dump(PR_TRUE, PR_TRUE, PR_TRUE);
2319 #endif
2320 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2321 NS_FRAME_HAS_DIRTY_CHILDREN);
2322 SetGeometryDirty();
2324 return NS_OK;
2327 NS_IMETHODIMP
2328 nsTableFrame::InsertFrames(nsIAtom* aListName,
2329 nsIFrame* aPrevFrame,
2330 nsIFrame* aFrameList)
2332 // Asssume there's only one frame being inserted. The problem is that
2333 // row group frames and col group frames go in separate child lists and
2334 // so if there's more than one type of frames this gets messy...
2335 // XXX The frame construction code should be separating out child frames
2336 // based on the type, bug 343048.
2338 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
2339 "inserting after sibling frame with different parent");
2341 // See what kind of frame we have
2342 const nsStyleDisplay* display = aFrameList->GetStyleDisplay();
2343 #ifdef DEBUG
2344 // verify that all sibling have the same type, if they do not, expect cellmap issues
2345 nsIFrame* nextFrame = aFrameList->GetNextSibling();
2346 while (nextFrame) {
2347 const nsStyleDisplay* nextDisplay = nextFrame->GetStyleDisplay();
2348 NS_ASSERTION((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) ==
2349 (nextDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP),
2350 "heterogenous childlist");
2351 nextFrame = nextFrame->GetNextSibling();
2353 #endif
2354 if (aPrevFrame) {
2355 const nsStyleDisplay* prevDisplay = aPrevFrame->GetStyleDisplay();
2356 // Make sure they belong on the same frame list
2357 if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) !=
2358 (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) {
2359 // the previous frame is not valid, see comment at ::AppendFrames
2360 // XXXbz Using content indices here means XBL will get screwed
2361 // over... Oh, well.
2362 nsIFrame* pseudoFrame = aFrameList;
2363 nsIContent* parentContent = GetContent();
2364 nsIContent* content;
2365 aPrevFrame = nsnull;
2366 while (pseudoFrame && (parentContent ==
2367 (content = pseudoFrame->GetContent()))) {
2368 pseudoFrame = pseudoFrame->GetFirstChild(nsnull);
2370 nsCOMPtr<nsIContent> container = content->GetParent();
2371 if (NS_LIKELY(container)) { // XXX need this null-check, see bug 411823.
2372 PRInt32 newIndex = container->IndexOf(content);
2373 nsIFrame* kidFrame;
2374 PRBool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ==
2375 display->mDisplay);
2376 nsTableColGroupFrame* lastColGroup;
2377 if (isColGroup) {
2378 kidFrame = mColGroups.FirstChild();
2379 nsTableColGroupFrame::GetLastRealColGroup(this,
2380 (nsIFrame**) &lastColGroup);
2382 else {
2383 kidFrame = mFrames.FirstChild();
2385 // Important: need to start at a value smaller than all valid indices
2386 PRInt32 lastIndex = -1;
2387 while (kidFrame) {
2388 if (isColGroup) {
2389 if (kidFrame == lastColGroup) {
2390 aPrevFrame = kidFrame; // there is no real colgroup after this one
2391 break;
2394 pseudoFrame = kidFrame;
2395 while (pseudoFrame && (parentContent ==
2396 (content = pseudoFrame->GetContent()))) {
2397 pseudoFrame = pseudoFrame->GetFirstChild(nsnull);
2399 PRInt32 index = container->IndexOf(content);
2400 if (index > lastIndex && index < newIndex) {
2401 lastIndex = index;
2402 aPrevFrame = kidFrame;
2404 kidFrame = kidFrame->GetNextSibling();
2409 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
2410 NS_ASSERTION(!aListName || aListName == nsGkAtoms::colGroupList,
2411 "unexpected child list");
2412 // Insert the column group frame
2413 nsFrameList frames(aFrameList); // convience for getting last frame
2414 nsIFrame* lastFrame = frames.LastChild();
2415 mColGroups.InsertFrames(nsnull, aPrevFrame, aFrameList);
2416 // find the starting col index for the first new col group
2417 PRInt32 startColIndex = 0;
2418 if (aPrevFrame) {
2419 nsTableColGroupFrame* prevColGroup =
2420 (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
2421 nsGkAtoms::tableColGroupFrame);
2422 if (prevColGroup) {
2423 startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
2426 InsertColGroups(startColIndex, aFrameList, lastFrame);
2427 } else if (IsRowGroup(display->mDisplay)) {
2428 NS_ASSERTION(!aListName, "unexpected child list");
2429 nsFrameList newList(aFrameList);
2430 nsIFrame* lastSibling = newList.LastChild();
2431 // Insert the frames in the sibling chain
2432 mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
2434 InsertRowGroups(aFrameList, lastSibling);
2435 } else {
2436 NS_ASSERTION(!aListName, "unexpected child list");
2437 // Just insert the frame and don't worry about reflowing it
2438 mFrames.InsertFrames(nsnull, aPrevFrame, aFrameList);
2439 return NS_OK;
2442 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2443 NS_FRAME_HAS_DIRTY_CHILDREN);
2444 SetGeometryDirty();
2445 #ifdef DEBUG_TABLE_CELLMAP
2446 printf("=== TableFrame::InsertFrames\n");
2447 Dump(PR_TRUE, PR_TRUE, PR_TRUE);
2448 #endif
2449 return NS_OK;
2452 NS_IMETHODIMP
2453 nsTableFrame::RemoveFrame(nsIAtom* aListName,
2454 nsIFrame* aOldFrame)
2456 // See what kind of frame we have
2457 const nsStyleDisplay* display = aOldFrame->GetStyleDisplay();
2459 // XXX The frame construction code should be separating out child frames
2460 // based on the type, bug 343048.
2461 if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
2462 NS_ASSERTION(!aListName || aListName == nsGkAtoms::colGroupList,
2463 "unexpected child list");
2464 nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
2465 nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
2466 PRInt32 firstColIndex = colGroup->GetStartColumnIndex();
2467 PRInt32 lastColIndex = firstColIndex + colGroup->GetColCount() - 1;
2468 mColGroups.DestroyFrame(aOldFrame);
2469 nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
2470 // remove the cols from the table
2471 PRInt32 colX;
2472 for (colX = lastColIndex; colX >= firstColIndex; colX--) {
2473 nsTableColFrame* colFrame = (nsTableColFrame*)mColFrames.SafeElementAt(colX);
2474 if (colFrame) {
2475 RemoveCol(colGroup, colX, PR_TRUE, PR_FALSE);
2479 PRInt32 numAnonymousColsToAdd = GetColCount() - mColFrames.Count();
2480 if (numAnonymousColsToAdd > 0) {
2481 // this sets the child list, updates the col cache and cell map
2482 CreateAnonymousColFrames(numAnonymousColsToAdd,
2483 eColAnonymousCell, PR_TRUE);
2486 } else {
2487 NS_ASSERTION(!aListName, "unexpected child list");
2488 nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aOldFrame);
2489 if (rgFrame) {
2490 // remove the row group from the cell map
2491 nsTableCellMap* cellMap = GetCellMap();
2492 if (cellMap) {
2493 cellMap->RemoveGroupCellMap(rgFrame);
2496 // remove the row group frame from the sibling chain
2497 mFrames.DestroyFrame(aOldFrame);
2499 // XXXldb [reflow branch merging 20060830] do we still need this?
2500 if (cellMap) {
2501 cellMap->Synchronize(this);
2502 ResetRowIndices();
2503 nsRect damageArea;
2504 cellMap->RebuildConsideringCells(nsnull, nsnull, 0, 0, PR_FALSE, damageArea);
2507 MatchCellMapToColCache(cellMap);
2508 } else {
2509 // Just remove the frame
2510 mFrames.DestroyFrame(aOldFrame);
2513 // for now, just bail and recalc all of the collapsing borders
2514 // XXXldb [reflow branch merging 20060830] do we still need this?
2515 if (IsBorderCollapse()) {
2516 nsRect damageArea(0, 0, PR_MAX(1, GetColCount()), PR_MAX(1, GetRowCount()));
2517 SetBCDamageArea(damageArea);
2519 PresContext()->PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange,
2520 NS_FRAME_HAS_DIRTY_CHILDREN);
2521 SetGeometryDirty();
2522 #ifdef DEBUG_TABLE_CELLMAP
2523 printf("=== TableFrame::RemoveFrame\n");
2524 Dump(PR_TRUE, PR_TRUE, PR_TRUE);
2525 #endif
2526 return NS_OK;
2529 /* virtual */ nsMargin
2530 nsTableFrame::GetUsedBorder() const
2532 if (!IsBorderCollapse())
2533 return nsHTMLContainerFrame::GetUsedBorder();
2535 return GetIncludedOuterBCBorder();
2538 /* virtual */ nsMargin
2539 nsTableFrame::GetUsedPadding() const
2541 if (!IsBorderCollapse())
2542 return nsHTMLContainerFrame::GetUsedPadding();
2544 return nsMargin(0,0,0,0);
2547 static void
2548 DivideBCBorderSize(nscoord aPixelSize,
2549 nscoord& aSmallHalf,
2550 nscoord& aLargeHalf)
2552 aSmallHalf = aPixelSize / 2;
2553 aLargeHalf = aPixelSize - aSmallHalf;
2556 nsMargin
2557 nsTableFrame::GetOuterBCBorder() const
2559 if (NeedToCalcBCBorders())
2560 const_cast<nsTableFrame*>(this)->CalcBCBorders();
2562 nsMargin border(0, 0, 0, 0);
2563 PRInt32 p2t = nsPresContext::AppUnitsPerCSSPixel();
2564 BCPropertyData* propData =
2565 (BCPropertyData*)nsTableFrame::GetProperty((nsIFrame*)this, nsGkAtoms::tableBCProperty, PR_FALSE);
2566 if (propData) {
2567 border.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
2568 border.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
2569 border.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
2570 border.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
2572 return border;
2575 nsMargin
2576 nsTableFrame::GetIncludedOuterBCBorder() const
2578 if (eCompatibility_NavQuirks == PresContext()->CompatibilityMode()) {
2579 return GetOuterBCBorder();
2581 nsMargin border(0, 0, 0, 0);
2582 return border;
2585 nsMargin
2586 nsTableFrame::GetExcludedOuterBCBorder() const
2588 if (eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
2589 return GetOuterBCBorder();
2591 nsMargin border(0, 0, 0, 0);
2592 return border;
2594 static
2595 void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState,
2596 nsStyleContext& aStyleContext,
2597 nsMargin& aBorderPadding)
2599 // XXXbz Either we _do_ have a reflow state and then we can use its
2600 // mComputedBorderPadding or we don't and then we get the padding
2601 // wrong!
2602 const nsStyleBorder* border = aStyleContext.GetStyleBorder();
2603 aBorderPadding = border->GetActualBorder();
2604 if (aReflowState) {
2605 aBorderPadding += aReflowState->mComputedPadding;
2609 nsMargin
2610 nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const
2612 nsMargin offset(0,0,0,0);
2613 if (IsBorderCollapse()) {
2614 nsPresContext* presContext = PresContext();
2615 if (eCompatibility_NavQuirks == presContext->CompatibilityMode()) {
2616 nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT1(offset);
2617 PRInt32 p2t = nsPresContext::AppUnitsPerCSSPixel();
2618 BCPropertyData* propData =
2619 (BCPropertyData*)nsTableFrame::GetProperty((nsIFrame*)firstInFlow, nsGkAtoms::tableBCProperty, PR_FALSE);
2620 if (!propData) ABORT1(offset);
2622 offset.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
2623 offset.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
2624 offset.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
2625 offset.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
2628 else {
2629 GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
2631 return offset;
2634 nsMargin
2635 nsTableFrame::GetContentAreaOffset(const nsHTMLReflowState* aReflowState) const
2637 nsMargin offset(0,0,0,0);
2638 if (IsBorderCollapse()) {
2639 // LDB: This used to unconditionally include the inner half as well,
2640 // but that's pretty clearly wrong per the CSS2.1 spec.
2641 offset = GetOuterBCBorder();
2643 else {
2644 GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
2646 return offset;
2649 void
2650 nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState)
2652 nsMargin collapseBorder;
2653 nsMargin padding(0,0,0,0);
2654 nsMargin* pCollapseBorder = nsnull;
2655 nsPresContext* presContext = PresContext();
2656 if (IsBorderCollapse()) {
2657 nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aReflowState.frame);
2658 if (rgFrame) {
2659 pCollapseBorder = rgFrame->GetBCBorderWidth(collapseBorder);
2662 aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding);
2664 NS_ASSERTION(!mBits.mResizedColumns ||
2665 !aReflowState.parentReflowState->mFlags.mSpecialHeightReflow,
2666 "should not resize columns on special height reflow");
2667 if (mBits.mResizedColumns) {
2668 aReflowState.mFlags.mHResize = PR_TRUE;
2672 // Position and size aKidFrame and update our reflow state. The origin of
2673 // aKidRect is relative to the upper-left origin of our frame
2674 void nsTableFrame::PlaceChild(nsTableReflowState& aReflowState,
2675 nsIFrame* aKidFrame,
2676 nsHTMLReflowMetrics& aKidDesiredSize,
2677 const nsRect& aOriginalKidRect,
2678 const nsRect& aOriginalKidOverflowRect)
2680 PRBool isFirstReflow =
2681 (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
2683 // Place and size the child
2684 FinishReflowChild(aKidFrame, PresContext(), nsnull, aKidDesiredSize,
2685 aReflowState.x, aReflowState.y, 0);
2687 InvalidateFrame(aKidFrame, aOriginalKidRect, aOriginalKidOverflowRect,
2688 isFirstReflow);
2690 // Adjust the running y-offset
2691 aReflowState.y += aKidDesiredSize.height;
2693 // If our height is constrained, then update the available height
2694 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
2695 aReflowState.availSize.height -= aKidDesiredSize.height;
2699 void
2700 nsTableFrame::OrderRowGroups(RowGroupArray& aChildren) const
2702 aChildren.Clear();
2703 nsTableRowGroupFrame* head = nsnull;
2704 nsTableRowGroupFrame* foot = nsnull;
2706 nsIFrame* kidFrame = mFrames.FirstChild();
2707 while (kidFrame) {
2708 const nsStyleDisplay* kidDisplay = kidFrame->GetStyleDisplay();
2709 nsTableRowGroupFrame* rowGroup = GetRowGroupFrame(kidFrame);
2710 if (NS_LIKELY(rowGroup)) {
2711 switch(kidDisplay->mDisplay) {
2712 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
2713 if (head) { // treat additional thead like tbody
2714 aChildren.AppendElement(rowGroup);
2716 else {
2717 head = rowGroup;
2719 break;
2720 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
2721 if (foot) { // treat additional tfoot like tbody
2722 aChildren.AppendElement(rowGroup);
2724 else {
2725 foot = rowGroup;
2727 break;
2728 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
2729 aChildren.AppendElement(rowGroup);
2730 break;
2731 default:
2732 NS_NOTREACHED("How did this produce an nsTableRowGroupFrame?");
2733 // Just ignore it
2734 break;
2737 // Get the next sibling but skip it if it's also the next-in-flow, since
2738 // a next-in-flow will not be part of the current table.
2739 while (kidFrame) {
2740 nsIFrame* nif = kidFrame->GetNextInFlow();
2741 kidFrame = kidFrame->GetNextSibling();
2742 if (kidFrame != nif)
2743 break;
2747 // put the thead first
2748 if (head) {
2749 aChildren.InsertElementAt(0, head);
2752 // put the tfoot after the last tbody
2753 if (foot) {
2754 aChildren.AppendElement(foot);
2758 PRUint32
2759 nsTableFrame::OrderRowGroups(FrameArray& aChildren,
2760 nsTableRowGroupFrame** aHead,
2761 nsTableRowGroupFrame** aFoot) const
2763 aChildren.Clear();
2764 // initialize out parameters
2765 *aHead = nsnull;
2766 *aFoot = nsnull;
2768 FrameArray nonRowGroups;
2770 nsIFrame* head = nsnull;
2771 nsIFrame* foot = nsnull;
2773 nsIFrame* kidFrame = mFrames.FirstChild();
2774 while (kidFrame) {
2775 const nsStyleDisplay* kidDisplay = kidFrame->GetStyleDisplay();
2776 nsTableRowGroupFrame* rowGroup = GetRowGroupFrame(kidFrame);
2777 if (NS_LIKELY(rowGroup)) {
2778 switch(kidDisplay->mDisplay) {
2779 case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
2780 if (head) { // treat additional thead like tbody
2781 aChildren.AppendElement(kidFrame);
2783 else {
2784 head = kidFrame;
2785 *aHead = rowGroup;
2787 break;
2788 case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
2789 if (foot) { // treat additional tfoot like tbody
2790 aChildren.AppendElement(kidFrame);
2792 else {
2793 foot = kidFrame;
2794 *aFoot = rowGroup;
2796 break;
2797 case NS_STYLE_DISPLAY_TABLE_ROW_GROUP:
2798 aChildren.AppendElement(kidFrame);
2799 break;
2800 default:
2801 break;
2803 } else {
2804 NS_NOTREACHED("Non-row-group primary frame list child of an "
2805 "nsTableFrame? How come?");
2806 nonRowGroups.AppendElement(kidFrame);
2809 // Get the next sibling but skip it if it's also the next-in-flow, since
2810 // a next-in-flow will not be part of the current table.
2811 while (kidFrame) {
2812 nsIFrame* nif = kidFrame->GetNextInFlow();
2813 kidFrame = kidFrame->GetNextSibling();
2814 if (kidFrame != nif)
2815 break;
2819 // put the thead first
2820 if (head) {
2821 aChildren.InsertElementAt(0, head);
2824 // put the tfoot after the last tbody
2825 if (foot) {
2826 aChildren.AppendElement(foot);
2829 PRUint32 rowGroupCount = aChildren.Length();
2830 aChildren.AppendElements(nonRowGroups);
2832 return rowGroupCount;
2835 nsTableRowGroupFrame*
2836 nsTableFrame::GetTHead() const
2838 nsIFrame* kidFrame = mFrames.FirstChild();
2839 while (kidFrame) {
2840 if (kidFrame->GetStyleDisplay()->mDisplay ==
2841 NS_STYLE_DISPLAY_TABLE_HEADER_GROUP) {
2842 nsTableRowGroupFrame* rg = GetRowGroupFrame(kidFrame);
2843 if (rg) {
2844 return rg;
2848 // Get the next sibling but skip it if it's also the next-in-flow, since
2849 // a next-in-flow will not be part of the current table.
2850 while (kidFrame) {
2851 nsIFrame* nif = kidFrame->GetNextInFlow();
2852 kidFrame = kidFrame->GetNextSibling();
2853 if (kidFrame != nif)
2854 break;
2858 return nsnull;
2861 nsTableRowGroupFrame*
2862 nsTableFrame::GetTFoot() const
2864 nsIFrame* kidFrame = mFrames.FirstChild();
2865 while (kidFrame) {
2866 if (kidFrame->GetStyleDisplay()->mDisplay ==
2867 NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP) {
2868 nsTableRowGroupFrame* rg = GetRowGroupFrame(kidFrame);
2869 if (rg) {
2870 return rg;
2874 // Get the next sibling but skip it if it's also the next-in-flow, since
2875 // a next-in-flow will not be part of the current table.
2876 while (kidFrame) {
2877 nsIFrame* nif = kidFrame->GetNextInFlow();
2878 kidFrame = kidFrame->GetNextSibling();
2879 if (kidFrame != nif)
2880 break;
2884 return nsnull;
2887 static PRBool
2888 IsRepeatable(nscoord aFrameHeight, nscoord aPageHeight)
2890 return aFrameHeight < (aPageHeight / 4);
2893 nsresult
2894 nsTableFrame::SetupHeaderFooterChild(const nsTableReflowState& aReflowState,
2895 nsTableRowGroupFrame* aFrame,
2896 nscoord* aDesiredHeight)
2898 nsPresContext* presContext = PresContext();
2899 nscoord pageHeight = presContext->GetPageSize().height;
2901 if (aFrame->GetParent() != this || pageHeight == NS_UNCONSTRAINEDSIZE) {
2902 // Must be a scrollable head/footer (we don't allow those to repeat), or
2903 // page has unconstrained height for some reason.
2904 *aDesiredHeight = 0;
2905 return NS_OK;
2908 // Reflow the child with unconstrainted height
2909 nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
2910 aFrame,
2911 nsSize(aReflowState.availSize.width, NS_UNCONSTRAINEDSIZE),
2912 -1, -1, PR_FALSE);
2913 InitChildReflowState(kidReflowState);
2914 kidReflowState.mFlags.mIsTopOfPage = PR_TRUE;
2915 nsHTMLReflowMetrics desiredSize;
2916 desiredSize.width = desiredSize.height = 0;
2917 nsReflowStatus status;
2918 nsresult rv = ReflowChild(aFrame, presContext, desiredSize, kidReflowState,
2919 aReflowState.x, aReflowState.y, 0, status);
2920 NS_ENSURE_SUCCESS(rv, rv);
2921 // The child will be reflowed again "for real" so no need to place it now
2923 aFrame->SetRepeatable(IsRepeatable(desiredSize.height, pageHeight));
2924 *aDesiredHeight = desiredSize.height;
2925 return NS_OK;
2928 // Reflow the children based on the avail size and reason in aReflowState
2929 // update aReflowMetrics a aStatus
2930 NS_METHOD
2931 nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
2932 nsReflowStatus& aStatus,
2933 nsIFrame*& aLastChildReflowed,
2934 nsRect& aOverflowArea)
2936 aStatus = NS_FRAME_COMPLETE;
2937 aLastChildReflowed = nsnull;
2939 nsIFrame* prevKidFrame = nsnull;
2940 nsresult rv = NS_OK;
2941 nscoord cellSpacingY = GetCellSpacingY();
2943 nsPresContext* presContext = PresContext();
2944 // XXXldb Should we be checking constrained height instead?
2945 PRBool isPaginated = presContext->IsPaginated();
2947 aOverflowArea = nsRect (0, 0, 0, 0);
2949 PRBool reflowAllKids = aReflowState.reflowState.ShouldReflowAllKids() ||
2950 mBits.mResizedColumns ||
2951 IsGeometryDirty();
2953 FrameArray rowGroups;
2954 nsTableRowGroupFrame *thead, *tfoot;
2955 PRUint32 numRowGroups = OrderRowGroups(rowGroups, &thead, &tfoot);
2956 PRBool pageBreak = PR_FALSE;
2957 nscoord footerHeight = 0;
2959 // Determine the repeatablility of headers and footers, and also the desired
2960 // height of any repeatable footer.
2961 // The repeatability of headers on continued tables is handled
2962 // when they are created in nsCSSFrameConstructor::CreateContinuingTableFrame.
2963 // We handle the repeatability of footers again here because we need to
2964 // determine the footer's height anyway. We could perhaps optimize by
2965 // using the footer's prev-in-flow's height instead of reflowing it again,
2966 // but there's no real need.
2967 if (isPaginated) {
2968 if (thead && !GetPrevInFlow()) {
2969 nscoord desiredHeight;
2970 rv = SetupHeaderFooterChild(aReflowState, thead, &desiredHeight);
2971 if (NS_FAILED(rv))
2972 return rv;
2974 if (tfoot) {
2975 rv = SetupHeaderFooterChild(aReflowState, tfoot, &footerHeight);
2976 if (NS_FAILED(rv))
2977 return rv;
2981 for (PRUint32 childX = 0; childX < numRowGroups; childX++) {
2982 nsIFrame* kidFrame = rowGroups[childX];
2983 // Get the frame state bits
2984 // See if we should only reflow the dirty child frames
2985 if (reflowAllKids ||
2986 NS_SUBTREE_DIRTY(kidFrame) ||
2987 (aReflowState.reflowState.mFlags.mSpecialHeightReflow &&
2988 (isPaginated || (kidFrame->GetStateBits() &
2989 NS_FRAME_CONTAINS_RELATIVE_HEIGHT)))) {
2990 if (pageBreak) {
2991 PushChildren(rowGroups, childX);
2992 aStatus = NS_FRAME_NOT_COMPLETE;
2993 break;
2996 nsSize kidAvailSize(aReflowState.availSize);
2997 // if the child is a tbody in paginated mode reduce the height by a repeated footer
2998 PRBool allowRepeatedFooter = PR_FALSE;
2999 if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) {
3000 nsTableRowGroupFrame* kidRG = GetRowGroupFrame(kidFrame);
3001 if (kidRG != thead && kidRG != tfoot && tfoot && tfoot->IsRepeatable()) {
3002 // the child is a tbody and there is a repeatable footer
3003 NS_ASSERTION(tfoot == rowGroups[rowGroups.Length() - 1], "Missing footer!");
3004 if (footerHeight + cellSpacingY < kidAvailSize.height) {
3005 allowRepeatedFooter = PR_TRUE;
3006 kidAvailSize.height -= footerHeight + cellSpacingY;
3011 nsRect oldKidRect = kidFrame->GetRect();
3012 nsRect oldKidOverflowRect = kidFrame->GetOverflowRect();
3014 nsHTMLReflowMetrics desiredSize;
3015 desiredSize.width = desiredSize.height = 0;
3017 // Reflow the child into the available space
3018 nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState,
3019 kidFrame, kidAvailSize,
3020 -1, -1, PR_FALSE);
3021 InitChildReflowState(kidReflowState);
3023 // If this isn't the first row group, and the previous row group has a
3024 // nonzero YMost, then we can't be at the top of the page.
3025 // We ignore the head row group in this check, because a head row group
3026 // may be automatically added at the top of *every* page. This prevents
3027 // infinite loops in some circumstances - see bug 344883.
3028 if (childX > (thead ? 1 : 0) &&
3029 (rowGroups[childX - 1]->GetRect().YMost() > 0)) {
3030 kidReflowState.mFlags.mIsTopOfPage = PR_FALSE;
3032 aReflowState.y += cellSpacingY;
3033 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
3034 aReflowState.availSize.height -= cellSpacingY;
3036 // record the presence of a next in flow, it might get destroyed so we
3037 // need to reorder the row group array
3038 nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
3039 PRBool reorder = PR_FALSE;
3040 if (kidFrame->GetNextInFlow())
3041 reorder = PR_TRUE;
3043 rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
3044 aReflowState.x, aReflowState.y,
3045 NS_FRAME_INVALIDATE_ON_MOVE, aStatus);
3047 if (reorder) {
3048 // reorder row groups the reflow may have changed the nextinflows
3049 numRowGroups = OrderRowGroups(rowGroups, &thead, &tfoot);
3050 childX = rowGroups.IndexOf(kidFrame);
3051 if (childX == RowGroupArray::NoIndex) {
3052 // XXXbz can this happen?
3053 childX = numRowGroups;
3056 // see if the rowgroup did not fit on this page might be pushed on
3057 // the next page
3058 if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
3059 (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight) &&
3060 kidReflowState.availableHeight < desiredSize.height) {
3061 // if we are on top of the page place with dataloss
3062 if (kidReflowState.mFlags.mIsTopOfPage) {
3063 if (childX+1 < rowGroups.Length()) {
3064 nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
3065 if (nextRowGroupFrame) {
3066 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
3067 oldKidOverflowRect);
3068 aStatus = NS_FRAME_NOT_COMPLETE;
3069 PushChildren(rowGroups, childX + 1);
3070 aLastChildReflowed = kidFrame;
3071 break;
3075 else { // we are not on top, push this rowgroup onto the next page
3076 if (prevKidFrame) { // we had a rowgroup before so push this
3077 // XXXroc shouldn't we add a repeated footer here?
3078 aStatus = NS_FRAME_NOT_COMPLETE;
3079 PushChildren(rowGroups, childX);
3080 aLastChildReflowed = prevKidFrame;
3081 break;
3086 aLastChildReflowed = kidFrame;
3088 pageBreak = PR_FALSE;
3089 // see if there is a page break after this row group or before the next one
3090 if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
3091 (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight)) {
3092 nsIFrame* nextKid =
3093 (childX + 1 < numRowGroups) ? rowGroups[childX + 1] : nsnull;
3094 pageBreak = PageBreakAfter(*kidFrame, nextKid);
3097 // Place the child
3098 PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
3099 oldKidOverflowRect);
3101 // Remember where we just were in case we end up pushing children
3102 prevKidFrame = kidFrame;
3104 // Special handling for incomplete children
3105 if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
3106 kidNextInFlow = kidFrame->GetNextInFlow();
3107 if (!kidNextInFlow) {
3108 // The child doesn't have a next-in-flow so create a continuing
3109 // frame. This hooks the child into the flow
3110 nsIFrame* continuingFrame;
3112 rv = presContext->PresShell()->FrameConstructor()->
3113 CreateContinuingFrame(presContext, kidFrame, this,
3114 &continuingFrame);
3115 if (NS_FAILED(rv)) {
3116 aStatus = NS_FRAME_COMPLETE;
3117 break;
3120 // Add the continuing frame to the sibling list
3121 continuingFrame->SetNextSibling(kidFrame->GetNextSibling());
3122 kidFrame->SetNextSibling(continuingFrame);
3123 // Update rowGroups with the new rowgroup, just as it
3124 // would have been if we had called OrderRowGroups
3125 // again. Note that rowGroups doesn't get used again after
3126 // we PushChildren below, anyway.
3127 rowGroups.InsertElementAt(childX + 1, continuingFrame);
3129 else {
3130 // put the nextinflow so that it will get pushed
3131 rowGroups.InsertElementAt(childX + 1, kidNextInFlow);
3133 // We've used up all of our available space so push the remaining
3134 // children to the next-in-flow
3135 nsIFrame* nextSibling = kidFrame->GetNextSibling();
3136 if (nsnull != nextSibling) {
3137 PushChildren(rowGroups, childX + 1);
3139 if (allowRepeatedFooter) {
3140 kidAvailSize.height = footerHeight;
3141 nsHTMLReflowState footerReflowState(presContext,
3142 aReflowState.reflowState,
3143 tfoot, kidAvailSize,
3144 -1, -1, PR_FALSE);
3145 InitChildReflowState(footerReflowState);
3146 aReflowState.y += cellSpacingY;
3148 nsRect origTfootRect = tfoot->GetRect();
3149 nsRect origTfootOverflowRect = tfoot->GetOverflowRect();
3151 nsReflowStatus footerStatus;
3152 rv = ReflowChild(tfoot, presContext, desiredSize, footerReflowState,
3153 aReflowState.x, aReflowState.y,
3154 NS_FRAME_INVALIDATE_ON_MOVE, footerStatus);
3155 PlaceChild(aReflowState, tfoot, desiredSize, origTfootRect,
3156 origTfootOverflowRect);
3158 break;
3161 else { // it isn't being reflowed
3162 aReflowState.y += cellSpacingY;
3163 nsRect kidRect = kidFrame->GetRect();
3164 if (kidRect.y != aReflowState.y) {
3165 // invalidate the old position
3166 kidFrame->InvalidateOverflowRect();
3167 kidRect.y = aReflowState.y;
3168 kidFrame->SetRect(kidRect); // move to the new position
3169 RePositionViews(kidFrame);
3170 // invalidate the new position
3171 kidFrame->InvalidateOverflowRect();
3173 aReflowState.y += kidRect.height;
3175 // If our height is constrained then update the available height.
3176 if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
3177 aReflowState.availSize.height -= cellSpacingY + kidRect.height;
3180 ConsiderChildOverflow(aOverflowArea, kidFrame);
3183 // We've now propagated the column resizes and geometry changes to all
3184 // the children.
3185 mBits.mResizedColumns = PR_FALSE;
3186 ClearGeometryDirty();
3188 return rv;
3191 void
3192 nsTableFrame::ReflowColGroups(nsIRenderingContext *aRenderingContext)
3194 if (!GetPrevInFlow() && !HaveReflowedColGroups()) {
3195 nsHTMLReflowMetrics kidMet;
3196 nsPresContext *presContext = PresContext();
3197 for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame;
3198 kidFrame = kidFrame->GetNextSibling()) {
3199 if (NS_SUBTREE_DIRTY(kidFrame)) {
3200 // The column groups don't care about dimensions or reflow states.
3201 nsHTMLReflowState kidReflowState(presContext, kidFrame,
3202 aRenderingContext, nsSize(0,0));
3203 nsReflowStatus cgStatus;
3204 ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0,
3205 cgStatus);
3206 FinishReflowChild(kidFrame, presContext, nsnull, kidMet, 0, 0, 0);
3209 SetHaveReflowedColGroups(PR_TRUE);
3213 void
3214 nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize)
3216 nsTableCellMap* cellMap = GetCellMap();
3217 if (!cellMap) {
3218 NS_ASSERTION(PR_FALSE, "never ever call me until the cell map is built!");
3219 aDesiredSize.height = 0;
3220 return;
3222 nscoord cellSpacingY = GetCellSpacingY();
3223 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
3225 // get the natural height based on the last child's (row group or scroll frame) rect
3226 FrameArray rowGroups;
3227 PRUint32 numRowGroups;
3229 // Scope for the dummies so we don't use them by accident
3230 nsTableRowGroupFrame *dummy1, *dummy2;
3231 numRowGroups = OrderRowGroups(rowGroups, &dummy1, &dummy2);
3233 if (numRowGroups == 0) {
3234 // tables can be used as rectangular items without content
3235 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
3236 if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) &&
3237 (tableSpecifiedHeight > 0) &&
3238 eCompatibility_NavQuirks != PresContext()->CompatibilityMode()) {
3239 // empty tables should not have a size in quirks mode
3240 aDesiredSize.height = tableSpecifiedHeight;
3242 else
3243 aDesiredSize.height = 0;
3244 return;
3246 PRInt32 rowCount = cellMap->GetRowCount();
3247 PRInt32 colCount = cellMap->GetColCount();
3248 nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
3249 if (rowCount > 0 && colCount > 0) {
3250 desiredHeight += cellSpacingY;
3251 for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
3252 desiredHeight += rowGroups[rgX]->GetSize().height + cellSpacingY;
3256 // see if a specified table height requires dividing additional space to rows
3257 if (!GetPrevInFlow()) {
3258 nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
3259 if ((tableSpecifiedHeight > 0) &&
3260 (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) &&
3261 (tableSpecifiedHeight > desiredHeight)) {
3262 // proportionately distribute the excess height to unconstrained rows in each
3263 // unconstrained row group.
3264 DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight);
3265 // this might have changed the overflow area incorporate the childframe overflow area.
3266 for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
3267 ConsiderChildOverflow(aDesiredSize.mOverflowArea, kidFrame);
3269 desiredHeight = tableSpecifiedHeight;
3272 aDesiredSize.height = desiredHeight;
3275 static
3276 void ResizeCells(nsTableFrame& aTableFrame)
3278 nsTableFrame::RowGroupArray rowGroups;
3279 aTableFrame.OrderRowGroups(rowGroups);
3280 nsHTMLReflowMetrics tableDesiredSize;
3281 nsRect tableRect = aTableFrame.GetRect();
3282 tableDesiredSize.width = tableRect.width;
3283 tableDesiredSize.height = tableRect.height;
3284 tableDesiredSize.mOverflowArea = nsRect(0, 0, tableRect.width,
3285 tableRect.height);
3287 for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
3288 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3290 nsRect rowGroupRect = rgFrame->GetRect();
3291 nsHTMLReflowMetrics groupDesiredSize;
3292 groupDesiredSize.width = rowGroupRect.width;
3293 groupDesiredSize.height = rowGroupRect.height;
3294 groupDesiredSize.mOverflowArea = nsRect(0, 0, groupDesiredSize.width,
3295 groupDesiredSize.height);
3296 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3297 while (rowFrame) {
3298 rowFrame->DidResize();
3299 rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowArea, rowFrame);
3300 rowFrame = rowFrame->GetNextRow();
3302 rgFrame->FinishAndStoreOverflow(&groupDesiredSize.mOverflowArea,
3303 nsSize(groupDesiredSize.width, groupDesiredSize.height));
3304 // make the coordinates of |desiredSize.mOverflowArea| incorrect
3305 // since it's about to go away:
3306 groupDesiredSize.mOverflowArea.MoveBy(rgFrame->GetPosition());
3307 tableDesiredSize.mOverflowArea.UnionRect(tableDesiredSize.mOverflowArea, groupDesiredSize.mOverflowArea);
3309 aTableFrame.FinishAndStoreOverflow(&tableDesiredSize.mOverflowArea,
3310 nsSize(tableDesiredSize.width, tableDesiredSize.height));
3313 void
3314 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
3315 nscoord aAmount)
3317 nscoord cellSpacingY = GetCellSpacingY();
3319 nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
3321 RowGroupArray rowGroups;
3322 OrderRowGroups(rowGroups);
3324 nscoord amountUsed = 0;
3325 // distribute space to each pct height row whose row group doesn't have a computed
3326 // height, and base the pct on the table height. If the row group had a computed
3327 // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights
3328 nscoord pctBasis = aReflowState.ComputedHeight() - (GetCellSpacingY() * (GetRowCount() + 1));
3329 nscoord yOriginRG = borderPadding.top + GetCellSpacingY();
3330 nscoord yEndRG = yOriginRG;
3331 PRUint32 rgX;
3332 for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3333 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3334 nscoord amountUsedByRG = 0;
3335 nscoord yOriginRow = 0;
3336 nsRect rgRect = rgFrame->GetRect();
3337 if (!rgFrame->HasStyleHeight()) {
3338 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3339 while (rowFrame) {
3340 nsRect rowRect = rowFrame->GetRect();
3341 if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
3342 nscoord pctHeight = rowFrame->GetHeight(pctBasis);
3343 nscoord amountForRow = PR_MIN(aAmount - amountUsed, pctHeight - rowRect.height);
3344 if (amountForRow > 0) {
3345 nsRect oldRowRect = rowRect;
3346 rowRect.height += amountForRow;
3347 // XXXbz we don't need to change rowRect.y to be yOriginRow?
3348 rowFrame->SetRect(rowRect);
3349 yOriginRow += rowRect.height + cellSpacingY;
3350 yEndRG += rowRect.height + cellSpacingY;
3351 amountUsed += amountForRow;
3352 amountUsedByRG += amountForRow;
3353 //rowFrame->DidResize();
3354 nsTableFrame::RePositionViews(rowFrame);
3356 rgFrame->InvalidateRectDifference(oldRowRect, rowRect);
3359 else {
3360 if (amountUsed > 0 && yOriginRow != rowRect.y &&
3361 !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
3362 rowFrame->InvalidateOverflowRect();
3363 rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
3364 nsTableFrame::RePositionViews(rowFrame);
3365 rowFrame->InvalidateOverflowRect();
3367 yOriginRow += rowRect.height + cellSpacingY;
3368 yEndRG += rowRect.height + cellSpacingY;
3370 rowFrame = rowFrame->GetNextRow();
3372 if (amountUsed > 0) {
3373 if (rgRect.y != yOriginRG) {
3374 rgFrame->InvalidateOverflowRect();
3377 nsRect origRgRect = rgRect;
3378 nsRect origRgOverflowRect = rgFrame->GetOverflowRect();
3380 rgRect.y = yOriginRG;
3381 rgRect.height += amountUsedByRG;
3383 rgFrame->SetRect(rgRect);
3385 nsTableFrame::InvalidateFrame(rgFrame, origRgRect, origRgOverflowRect,
3386 PR_FALSE);
3389 else if (amountUsed > 0 && yOriginRG != rgRect.y) {
3390 rgFrame->InvalidateOverflowRect();
3391 rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
3392 // Make sure child views are properly positioned
3393 nsTableFrame::RePositionViews(rgFrame);
3394 rgFrame->InvalidateOverflowRect();
3396 yOriginRG = yEndRG;
3399 if (amountUsed >= aAmount) {
3400 ResizeCells(*this);
3401 return;
3404 // get the first row without a style height where its row group has an
3405 // unconstrained height
3406 nsTableRowGroupFrame* firstUnStyledRG = nsnull;
3407 nsTableRowFrame* firstUnStyledRow = nsnull;
3408 for (rgX = 0; rgX < rowGroups.Length() && !firstUnStyledRG; rgX++) {
3409 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3410 if (!rgFrame->HasStyleHeight()) {
3411 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3412 while (rowFrame) {
3413 if (!rowFrame->HasStyleHeight()) {
3414 firstUnStyledRG = rgFrame;
3415 firstUnStyledRow = rowFrame;
3416 break;
3418 rowFrame = rowFrame->GetNextRow();
3423 nsTableRowFrame* lastEligibleRow = nsnull;
3424 // Accumulate the correct divisor. This will be the total total height of all
3425 // unstyled rows inside unstyled row groups, unless there are none, in which
3426 // case, it will be number of all rows. If the unstyled rows don't have a
3427 // height, divide the space equally among them.
3428 nscoord divisor = 0;
3429 PRInt32 eligibleRows = 0;
3430 PRBool expandEmptyRows = PR_FALSE;
3432 if (!firstUnStyledRow) {
3433 // there is no unstyled row
3434 divisor = GetRowCount();
3436 else {
3437 for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3438 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3439 if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) {
3440 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3441 while (rowFrame) {
3442 if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) {
3443 NS_ASSERTION(rowFrame->GetSize().height >= 0,
3444 "negative row frame height");
3445 divisor += rowFrame->GetSize().height;
3446 eligibleRows++;
3447 lastEligibleRow = rowFrame;
3449 rowFrame = rowFrame->GetNextRow();
3453 if (divisor <= 0) {
3454 if (eligibleRows > 0) {
3455 expandEmptyRows = PR_TRUE;
3457 else {
3458 NS_ERROR("invalid divisor");
3459 return;
3463 // allocate the extra height to the unstyled row groups and rows
3464 nscoord heightToDistribute = aAmount - amountUsed;
3465 yOriginRG = borderPadding.top + cellSpacingY;
3466 yEndRG = yOriginRG;
3467 for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
3468 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
3469 nscoord amountUsedByRG = 0;
3470 nscoord yOriginRow = 0;
3471 nsRect rgRect = rgFrame->GetRect();
3472 nsRect rgOverflowRect = rgFrame->GetOverflowRect();
3473 // see if there is an eligible row group or we distribute to all rows
3474 if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) {
3475 nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
3476 while (rowFrame) {
3477 nsRect rowRect = rowFrame->GetRect();
3478 nsRect rowOverflowRect = rowFrame->GetOverflowRect();
3479 // see if there is an eligible row or we distribute to all rows
3480 if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) {
3481 float ratio;
3482 if (eligibleRows) {
3483 if (!expandEmptyRows) {
3484 // The amount of additional space each row gets is proportional to
3485 // its height
3486 ratio = float(rowRect.height) / float(divisor);
3487 } else {
3488 // empty rows get all the same additional space
3489 ratio = 1.0f / float(eligibleRows);
3492 else {
3493 // all rows get the same additional space
3494 ratio = 1.0f / float(divisor);
3496 // give rows their additional space, except for the last row which
3497 // gets the remainder
3498 nscoord amountForRow = (rowFrame == lastEligibleRow)
3499 ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio);
3500 amountForRow = PR_MIN(amountForRow, aAmount - amountUsed);
3502 if (yOriginRow != rowRect.y) {
3503 rowFrame->InvalidateOverflowRect();
3506 // update the row height
3507 nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width,
3508 rowRect.height + amountForRow);
3509 rowFrame->SetRect(newRowRect);
3511 yOriginRow += newRowRect.height + cellSpacingY;
3512 yEndRG += newRowRect.height + cellSpacingY;
3514 amountUsed += amountForRow;
3515 amountUsedByRG += amountForRow;
3516 NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
3517 //rowFrame->DidResize();
3518 nsTableFrame::RePositionViews(rowFrame);
3520 nsTableFrame::InvalidateFrame(rowFrame, rowRect, rowOverflowRect,
3521 PR_FALSE);
3523 else {
3524 if (amountUsed > 0 && yOriginRow != rowRect.y) {
3525 rowFrame->InvalidateOverflowRect();
3526 rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
3527 nsTableFrame::RePositionViews(rowFrame);
3528 rowFrame->InvalidateOverflowRect();
3530 yOriginRow += rowRect.height + cellSpacingY;
3531 yEndRG += rowRect.height + cellSpacingY;
3533 rowFrame = rowFrame->GetNextRow();
3535 if (amountUsed > 0) {
3536 if (rgRect.y != yOriginRG) {
3537 rgFrame->InvalidateOverflowRect();
3540 rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width,
3541 rgRect.height + amountUsedByRG));
3543 nsTableFrame::InvalidateFrame(rgFrame, rgRect, rgOverflowRect,
3544 PR_FALSE);
3546 // Make sure child views are properly positioned
3547 // XXX what happens if childFrame is a scroll frame and this gets skipped? see also below
3549 else if (amountUsed > 0 && yOriginRG != rgRect.y) {
3550 rgFrame->InvalidateOverflowRect();
3551 rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
3552 // Make sure child views are properly positioned
3553 nsTableFrame::RePositionViews(rgFrame);
3554 rgFrame->InvalidateOverflowRect();
3556 yOriginRG = yEndRG;
3559 ResizeCells(*this);
3562 PRBool
3563 nsTableFrame::IsPctHeight(nsStyleContext* aStyleContext)
3565 PRBool result = PR_FALSE;
3566 if (aStyleContext) {
3567 result = (eStyleUnit_Percent ==
3568 aStyleContext->GetStylePosition()->mHeight.GetUnit());
3570 return result;
3573 PRInt32 nsTableFrame::GetColumnWidth(PRInt32 aColIndex)
3575 nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
3576 NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
3577 PRInt32 result = 0;
3578 if (this == firstInFlow) {
3579 nsTableColFrame* colFrame = GetColFrame(aColIndex);
3580 if (colFrame) {
3581 result = colFrame->GetFinalWidth();
3584 else {
3585 result = firstInFlow->GetColumnWidth(aColIndex);
3588 return result;
3591 void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth)
3593 nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
3594 NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
3596 if (this == firstInFlow) {
3597 nsTableColFrame* colFrame = GetColFrame(aColIndex);
3598 if (colFrame) {
3599 colFrame->SetFinalWidth(aWidth);
3601 else {
3602 NS_ASSERTION(PR_FALSE, "null col frame");
3605 else {
3606 firstInFlow->SetColumnWidth(aColIndex, aWidth);
3610 // XXX: could cache this. But be sure to check style changes if you do!
3611 nscoord nsTableFrame::GetCellSpacingX()
3613 if (IsBorderCollapse())
3614 return 0;
3616 return GetStyleTableBorder()->mBorderSpacingX;
3619 // XXX: could cache this. But be sure to check style changes if you do!
3620 nscoord nsTableFrame::GetCellSpacingY()
3622 if (IsBorderCollapse())
3623 return 0;
3625 return GetStyleTableBorder()->mBorderSpacingY;
3629 /* virtual */ nscoord
3630 nsTableFrame::GetBaseline() const
3632 nscoord ascent = 0;
3633 RowGroupArray orderedRowGroups;
3634 OrderRowGroups(orderedRowGroups);
3635 nsTableRowFrame* firstRow = nsnull;
3636 for (PRUint32 rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
3637 // XXXbz Do we really want to just let through the scrollable
3638 // rowgroups and use their ascent?
3639 nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
3640 if (rgFrame->GetRowCount()) {
3641 firstRow = rgFrame->GetFirstRow();
3642 ascent = rgFrame->GetRect().y + firstRow->GetRect().y + firstRow->GetRowBaseline();
3643 break;
3646 if (!firstRow)
3647 ascent = GetRect().height;
3648 return ascent;
3650 /* ----- global methods ----- */
3652 nsIFrame*
3653 NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
3655 return new (aPresShell) nsTableFrame(aContext);
3658 nsTableFrame*
3659 nsTableFrame::GetTableFrame(nsIFrame* aSourceFrame)
3661 if (aSourceFrame) {
3662 // "result" is the result of intermediate calls, not the result we return from this method
3663 for (nsIFrame* parentFrame = aSourceFrame->GetParent(); parentFrame;
3664 parentFrame = parentFrame->GetParent()) {
3665 if (nsGkAtoms::tableFrame == parentFrame->GetType()) {
3666 return (nsTableFrame*)parentFrame;
3670 NS_NOTREACHED("unable to find table parent");
3671 return nsnull;
3674 PRBool
3675 nsTableFrame::IsAutoWidth(PRBool* aIsPctWidth)
3677 const nsStyleCoord& width = GetStylePosition()->mWidth;
3679 if (aIsPctWidth) {
3680 // XXX The old code also made the return value true for 0%, but that
3681 // seems silly.
3682 *aIsPctWidth = width.GetUnit() == eStyleUnit_Percent &&
3683 width.GetPercentValue() > 0.0f;
3684 // Should this handle -moz-available and -moz-fit-content?
3686 return width.GetUnit() == eStyleUnit_Auto;
3689 PRBool
3690 nsTableFrame::IsAutoHeight()
3692 PRBool isAuto = PR_TRUE; // the default
3694 const nsStylePosition* position = GetStylePosition();
3696 switch (position->mHeight.GetUnit()) {
3697 case eStyleUnit_Auto: // specified auto width
3698 break;
3699 case eStyleUnit_Coord:
3700 isAuto = PR_FALSE;
3701 break;
3702 case eStyleUnit_Percent:
3703 if (position->mHeight.GetPercentValue() > 0.0f) {
3704 isAuto = PR_FALSE;
3706 break;
3707 default:
3708 break;
3711 return isAuto;
3714 nscoord
3715 nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState)
3717 nscoord height = aState.ComputedHeight();
3718 if (NS_AUTOHEIGHT != height) {
3719 nsMargin borderPadding = GetContentAreaOffset(&aState);
3720 height += borderPadding.top + borderPadding.bottom;
3722 height = PR_MAX(0, height);
3724 return height;
3727 PRBool
3728 nsTableFrame::IsAutoLayout()
3730 if (GetStyleTable()->mLayoutStrategy == NS_STYLE_TABLE_LAYOUT_AUTO)
3731 return PR_TRUE;
3732 // a fixed-layout inline-table must have a width
3733 // and tables with 'width: -moz-max-content' must be auto-layout
3734 // (at least as long as FixedTableLayoutStrategy::GetPrefWidth returns
3735 // nscoord_MAX)
3736 const nsStyleCoord &width = GetStylePosition()->mWidth;
3737 return (width.GetUnit() == eStyleUnit_Auto) ||
3738 (width.GetUnit() == eStyleUnit_Enumerated &&
3739 width.GetIntValue() == NS_STYLE_WIDTH_MAX_CONTENT);
3742 #ifdef DEBUG
3743 NS_IMETHODIMP
3744 nsTableFrame::GetFrameName(nsAString& aResult) const
3746 return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
3748 #endif
3750 // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
3751 // is of type aChildType
3752 nsIFrame*
3753 nsTableFrame::GetFrameAtOrBefore(nsIFrame* aParentFrame,
3754 nsIFrame* aPriorChildFrame,
3755 nsIAtom* aChildType)
3757 nsIFrame* result = nsnull;
3758 if (!aPriorChildFrame) {
3759 return result;
3761 if (aChildType == aPriorChildFrame->GetType()) {
3762 return aPriorChildFrame;
3765 // aPriorChildFrame is not of type aChildType, so we need start from
3766 // the beginnng and find the closest one
3767 nsIFrame* lastMatchingFrame = nsnull;
3768 nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
3769 while (childFrame && (childFrame != aPriorChildFrame)) {
3770 if (aChildType == childFrame->GetType()) {
3771 lastMatchingFrame = childFrame;
3773 childFrame = childFrame->GetNextSibling();
3775 return lastMatchingFrame;
3778 #ifdef DEBUG
3779 void
3780 nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
3782 nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aKidFrame);
3783 if (rgFrame) {
3784 nsIFrame* rowFrame = rgFrame->GetFirstChild(nsnull);
3785 while (rowFrame) {
3786 if (nsGkAtoms::tableRowFrame == rowFrame->GetType()) {
3787 printf("row(%d)=%p ", ((nsTableRowFrame*)rowFrame)->GetRowIndex(), rowFrame);
3788 nsIFrame* cellFrame = rowFrame->GetFirstChild(nsnull);
3789 while (cellFrame) {
3790 if (IS_TABLE_CELL(cellFrame->GetType())) {
3791 PRInt32 colIndex;
3792 ((nsTableCellFrame*)cellFrame)->GetColIndex(colIndex);
3793 printf("cell(%d)=%p ", colIndex, cellFrame);
3795 cellFrame = cellFrame->GetNextSibling();
3797 printf("\n");
3799 else {
3800 DumpRowGroup(rowFrame);
3802 rowFrame = rowFrame->GetNextSibling();
3807 void
3808 nsTableFrame::Dump(PRBool aDumpRows,
3809 PRBool aDumpCols,
3810 PRBool aDumpCellMap)
3812 printf("***START TABLE DUMP*** \n");
3813 // dump the columns widths array
3814 printf("mColWidths=");
3815 PRInt32 numCols = GetColCount();
3816 PRInt32 colX;
3817 for (colX = 0; colX < numCols; colX++) {
3818 printf("%d ", GetColumnWidth(colX));
3820 printf("\n");
3822 if (aDumpRows) {
3823 nsIFrame* kidFrame = mFrames.FirstChild();
3824 while (kidFrame) {
3825 DumpRowGroup(kidFrame);
3826 kidFrame = kidFrame->GetNextSibling();
3830 if (aDumpCols) {
3831 // output col frame cache
3832 printf("\n col frame cache ->");
3833 for (colX = 0; colX < numCols; colX++) {
3834 nsTableColFrame* colFrame = (nsTableColFrame *)mColFrames.ElementAt(colX);
3835 if (0 == (colX % 8)) {
3836 printf("\n");
3838 printf ("%d=%p ", colX, colFrame);
3839 nsTableColType colType = colFrame->GetColType();
3840 switch (colType) {
3841 case eColContent:
3842 printf(" content ");
3843 break;
3844 case eColAnonymousCol:
3845 printf(" anonymous-column ");
3846 break;
3847 case eColAnonymousColGroup:
3848 printf(" anonymous-colgroup ");
3849 break;
3850 case eColAnonymousCell:
3851 printf(" anonymous-cell ");
3852 break;
3855 printf("\n colgroups->");
3856 for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame;
3857 childFrame = childFrame->GetNextSibling()) {
3858 if (nsGkAtoms::tableColGroupFrame == childFrame->GetType()) {
3859 nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
3860 colGroupFrame->Dump(1);
3863 for (colX = 0; colX < numCols; colX++) {
3864 printf("\n");
3865 nsTableColFrame* colFrame = GetColFrame(colX);
3866 colFrame->Dump(1);
3869 if (aDumpCellMap) {
3870 nsTableCellMap* cellMap = GetCellMap();
3871 cellMap->Dump();
3873 printf(" ***END TABLE DUMP*** \n");
3875 #endif
3877 // nsTableIterator
3878 nsTableIterator::nsTableIterator(nsIFrame& aSource)
3880 nsIFrame* firstChild = aSource.GetFirstChild(nsnull);
3881 Init(firstChild);
3884 nsTableIterator::nsTableIterator(nsFrameList& aSource)
3886 nsIFrame* firstChild = aSource.FirstChild();
3887 Init(firstChild);
3890 void nsTableIterator::Init(nsIFrame* aFirstChild)
3892 mFirstListChild = aFirstChild;
3893 mFirstChild = aFirstChild;
3894 mCurrentChild = nsnull;
3895 mLeftToRight = PR_TRUE;
3896 mCount = -1;
3898 if (!mFirstChild) {
3899 return;
3902 nsTableFrame* table = nsTableFrame::GetTableFrame(mFirstChild);
3903 if (table) {
3904 mLeftToRight = (NS_STYLE_DIRECTION_LTR ==
3905 table->GetStyleVisibility()->mDirection);
3907 else {
3908 NS_NOTREACHED("source of table iterator is not part of a table");
3909 return;
3912 if (!mLeftToRight) {
3913 mCount = 0;
3914 nsIFrame* nextChild = mFirstChild->GetNextSibling();
3915 while (nsnull != nextChild) {
3916 mCount++;
3917 mFirstChild = nextChild;
3918 nextChild = nextChild->GetNextSibling();
3923 nsIFrame* nsTableIterator::First()
3925 mCurrentChild = mFirstChild;
3926 return mCurrentChild;
3929 nsIFrame* nsTableIterator::Next()
3931 if (!mCurrentChild) {
3932 return nsnull;
3935 if (mLeftToRight) {
3936 mCurrentChild = mCurrentChild->GetNextSibling();
3937 return mCurrentChild;
3939 else {
3940 nsIFrame* targetChild = mCurrentChild;
3941 mCurrentChild = nsnull;
3942 nsIFrame* child = mFirstListChild;
3943 while (child && (child != targetChild)) {
3944 mCurrentChild = child;
3945 child = child->GetNextSibling();
3947 return mCurrentChild;
3951 PRBool nsTableIterator::IsLeftToRight()
3953 return mLeftToRight;
3956 PRInt32 nsTableIterator::Count()
3958 if (-1 == mCount) {
3959 mCount = 0;
3960 nsIFrame* child = mFirstListChild;
3961 while (nsnull != child) {
3962 mCount++;
3963 child = child->GetNextSibling();
3966 return mCount;
3969 /*------------------ nsITableLayout methods ------------------------------*/
3970 NS_IMETHODIMP
3971 nsTableFrame::GetCellDataAt(PRInt32 aRowIndex,
3972 PRInt32 aColIndex,
3973 nsIDOMElement* &aCell, //out params
3974 PRInt32& aStartRowIndex,
3975 PRInt32& aStartColIndex,
3976 PRInt32& aRowSpan,
3977 PRInt32& aColSpan,
3978 PRInt32& aActualRowSpan,
3979 PRInt32& aActualColSpan,
3980 PRBool& aIsSelected)
3982 // Initialize out params
3983 aCell = nsnull;
3984 aStartRowIndex = 0;
3985 aStartColIndex = 0;
3986 aRowSpan = 0;
3987 aColSpan = 0;
3988 aIsSelected = PR_FALSE;
3990 nsTableCellMap* cellMap = GetCellMap();
3991 if (!cellMap) { return NS_ERROR_NOT_INITIALIZED;}
3993 PRBool originates;
3994 PRInt32 colSpan; // Is this the "effective" or "html" value?
3996 nsTableCellFrame *cellFrame = cellMap->GetCellInfoAt(aRowIndex, aColIndex, &originates, &colSpan);
3997 if (!cellFrame) return NS_TABLELAYOUT_CELL_NOT_FOUND;
3999 nsresult result= cellFrame->GetRowIndex(aStartRowIndex);
4000 if (NS_FAILED(result)) return result;
4001 result = cellFrame->GetColIndex(aStartColIndex);
4002 if (NS_FAILED(result)) return result;
4003 //This returns HTML value, which may be 0
4004 aRowSpan = cellFrame->GetRowSpan();
4005 aColSpan = cellFrame->GetColSpan();
4006 aActualRowSpan = GetEffectiveRowSpan(*cellFrame);
4007 aActualColSpan = GetEffectiveColSpan(*cellFrame);
4009 // If these aren't at least 1, we have a cellmap error
4010 if (aActualRowSpan == 0 || aActualColSpan == 0)
4011 return NS_ERROR_FAILURE;
4013 result = cellFrame->GetSelected(&aIsSelected);
4014 if (NS_FAILED(result)) return result;
4016 // do this last, because it addrefs,
4017 // and we don't want the caller leaking it on error
4018 nsIContent* content = cellFrame->GetContent();
4019 if (!content) return NS_ERROR_FAILURE;
4021 return CallQueryInterface(content, &aCell);
4024 NS_IMETHODIMP nsTableFrame::GetTableSize(PRInt32& aRowCount, PRInt32& aColCount)
4026 nsTableCellMap* cellMap = GetCellMap();
4027 // Initialize out params
4028 aRowCount = 0;
4029 aColCount = 0;
4030 if (!cellMap) { return NS_ERROR_NOT_INITIALIZED;}
4032 aRowCount = cellMap->GetRowCount();
4033 aColCount = cellMap->GetColCount();
4034 return NS_OK;
4037 NS_IMETHODIMP
4038 nsTableFrame::GetIndexByRowAndColumn(PRInt32 aRow, PRInt32 aColumn,
4039 PRInt32 *aIndex)
4041 NS_ENSURE_ARG_POINTER(aIndex);
4042 *aIndex = -1;
4044 nsTableCellMap* cellMap = GetCellMap();
4045 if (!cellMap)
4046 return NS_ERROR_NOT_INITIALIZED;
4048 *aIndex = cellMap->GetIndexByRowAndColumn(aRow, aColumn);
4049 return NS_OK;
4052 NS_IMETHODIMP
4053 nsTableFrame::GetRowAndColumnByIndex(PRInt32 aIndex,
4054 PRInt32 *aRow, PRInt32 *aColumn)
4056 NS_ENSURE_ARG_POINTER(aRow);
4057 *aRow = -1;
4059 NS_ENSURE_ARG_POINTER(aColumn);
4060 *aColumn = -1;
4062 nsTableCellMap* cellMap = GetCellMap();
4063 if (!cellMap)
4064 return NS_ERROR_NOT_INITIALIZED;
4066 cellMap->GetRowAndColumnByIndex(aIndex, aRow, aColumn);
4067 return NS_OK;
4070 /*---------------- end of nsITableLayout implementation ------------------*/
4072 PRBool
4073 nsTableFrame::ColumnHasCellSpacingBefore(PRInt32 aColIndex) const
4075 // Since fixed-layout tables should not have their column sizes change
4076 // as they load, we assume that all columns are significant.
4077 if (LayoutStrategy()->GetType() == nsITableLayoutStrategy::Fixed)
4078 return PR_TRUE;
4079 nsTableCellMap* cellMap = GetCellMap();
4080 if (!cellMap)
4081 return PR_FALSE;
4082 return cellMap->GetNumCellsOriginatingInCol(aColIndex) > 0;
4085 static void
4086 CheckFixDamageArea(PRInt32 aNumRows,
4087 PRInt32 aNumCols,
4088 nsRect& aDamageArea)
4090 if (((aDamageArea.XMost() > aNumCols) && (aDamageArea.width != 1) && (aNumCols != 0)) ||
4091 ((aDamageArea.YMost() > aNumRows) && (aDamageArea.height != 1) && (aNumRows != 0))) {
4092 // the damage area was set incorrectly, just be safe and make it the entire table
4093 NS_ASSERTION(PR_FALSE, "invalid BC damage area");
4094 aDamageArea.x = 0;
4095 aDamageArea.y = 0;
4096 aDamageArea.width = aNumCols;
4097 aDamageArea.height = aNumRows;
4101 /********************************************************************************
4102 * Collapsing Borders
4104 * The CSS spec says to resolve border conflicts in this order:
4105 * 1) any border with the style HIDDEN wins
4106 * 2) the widest border with a style that is not NONE wins
4107 * 3) the border styles are ranked in this order, highest to lowest precedence:
4108 * double, solid, dashed, dotted, ridge, outset, groove, inset
4109 * 4) borders that are of equal width and style (differ only in color) have this precedence:
4110 * cell, row, rowgroup, col, colgroup, table
4111 * 5) if all border styles are NONE, then that's the computed border style.
4112 *******************************************************************************/
4114 void
4115 nsTableFrame::SetBCDamageArea(const nsRect& aValue)
4117 nsRect newRect(aValue);
4118 newRect.width = PR_MAX(1, newRect.width);
4119 newRect.height = PR_MAX(1, newRect.height);
4121 if (!IsBorderCollapse()) {
4122 NS_ASSERTION(PR_FALSE, "invalid call - not border collapse model");
4123 return;
4125 SetNeedToCalcBCBorders(PR_TRUE);
4126 // Get the property
4127 BCPropertyData* value = (BCPropertyData*)nsTableFrame::GetProperty(this, nsGkAtoms::tableBCProperty, PR_TRUE);
4128 if (value) {
4129 // for now just construct a union of the new and old damage areas
4130 value->mDamageArea.UnionRect(value->mDamageArea, newRect);
4131 CheckFixDamageArea(GetRowCount(), GetColCount(), value->mDamageArea);
4134 /*****************************************************************
4135 * BCMapCellIterator
4136 ****************************************************************/
4137 struct BCMapCellInfo
4139 BCMapCellInfo();
4140 void Reset();
4142 CellData* cellData;
4143 nsCellMap* cellMap;
4145 nsTableRowGroupFrame* rg;
4147 nsTableRowFrame* topRow;
4148 nsTableRowFrame* bottomRow;
4150 nsTableColGroupFrame* cg;
4152 nsTableColFrame* leftCol;
4153 nsTableColFrame* rightCol;
4155 nsBCTableCellFrame* cell;
4157 PRInt32 rowIndex;
4158 PRInt32 rowSpan;
4159 PRInt32 colIndex;
4160 PRInt32 colSpan;
4162 PRPackedBool rgTop;
4163 PRPackedBool rgBottom;
4164 PRPackedBool cgLeft;
4165 PRPackedBool cgRight;
4168 BCMapCellInfo::BCMapCellInfo()
4170 Reset();
4173 void BCMapCellInfo::Reset()
4175 cellData = nsnull;
4176 rg = nsnull;
4177 topRow = nsnull;
4178 bottomRow = nsnull;
4179 cg = nsnull;
4180 leftCol = nsnull;
4181 rightCol = nsnull;
4182 cell = nsnull;
4183 rowIndex = rowSpan = colIndex = colSpan = 0;
4184 rgTop = rgBottom = cgLeft = cgRight = PR_FALSE;
4187 class BCMapCellIterator
4189 public:
4190 BCMapCellIterator(nsTableFrame& aTableFrame,
4191 const nsRect& aDamageArea);
4193 void First(BCMapCellInfo& aMapCellInfo);
4195 void Next(BCMapCellInfo& aMapCellInfo);
4197 void PeekRight(BCMapCellInfo& aRefInfo,
4198 PRUint32 aRowIndex,
4199 BCMapCellInfo& aAjaInfo);
4201 void PeekBottom(BCMapCellInfo& aRefInfo,
4202 PRUint32 aColIndex,
4203 BCMapCellInfo& aAjaInfo);
4205 PRBool IsNewRow() { return mIsNewRow; }
4207 nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
4209 PRInt32 mRowGroupStart;
4210 PRInt32 mRowGroupEnd;
4211 PRBool mAtEnd;
4212 nsCellMap* mCellMap;
4214 private:
4215 void SetInfo(nsTableRowFrame* aRow,
4216 PRInt32 aColIndex,
4217 CellData* aCellData,
4218 BCMapCellInfo& aMapInfo,
4219 nsCellMap* aCellMap = nsnull);
4221 PRBool SetNewRow(nsTableRowFrame* row = nsnull);
4222 PRBool SetNewRowGroup(PRBool aFindFirstDamagedRow);
4224 nsTableFrame& mTableFrame;
4225 nsTableCellMap* mTableCellMap;
4226 nsTableFrame::RowGroupArray mRowGroups;
4227 nsTableRowGroupFrame* mRowGroup;
4228 PRInt32 mRowGroupIndex;
4229 PRUint32 mNumRows;
4230 nsTableRowFrame* mRow;
4231 nsTableRowFrame* mPrevRow;
4232 PRBool mIsNewRow;
4233 PRInt32 mRowIndex;
4234 PRUint32 mNumCols;
4235 PRInt32 mColIndex;
4236 nsPoint mAreaStart;
4237 nsPoint mAreaEnd;
4240 BCMapCellIterator::BCMapCellIterator(nsTableFrame& aTableFrame,
4241 const nsRect& aDamageArea)
4242 :mTableFrame(aTableFrame)
4244 mTableCellMap = aTableFrame.GetCellMap();
4246 mAreaStart.x = aDamageArea.x;
4247 mAreaStart.y = aDamageArea.y;
4248 mAreaEnd.y = aDamageArea.y + aDamageArea.height - 1;
4249 mAreaEnd.x = aDamageArea.x + aDamageArea.width - 1;
4251 mNumRows = mTableFrame.GetRowCount();
4252 mRow = nsnull;
4253 mRowIndex = 0;
4254 mNumCols = mTableFrame.GetColCount();
4255 mColIndex = 0;
4256 mRowGroupIndex = -1;
4258 // Get the ordered row groups
4259 aTableFrame.OrderRowGroups(mRowGroups);
4261 mAtEnd = PR_TRUE; // gets reset when First() is called
4264 void
4265 BCMapCellIterator::SetInfo(nsTableRowFrame* aRow,
4266 PRInt32 aColIndex,
4267 CellData* aCellData,
4268 BCMapCellInfo& aCellInfo,
4269 nsCellMap* aCellMap)
4271 aCellInfo.cellData = aCellData;
4272 aCellInfo.cellMap = (aCellMap) ? aCellMap : mCellMap;
4273 aCellInfo.colIndex = aColIndex;
4275 // row frame info
4276 aCellInfo.rowIndex = 0;
4277 if (aRow) {
4278 aCellInfo.topRow = aRow;
4279 aCellInfo.rowIndex = aRow->GetRowIndex();
4282 // cell frame info
4283 aCellInfo.cell = nsnull;
4284 aCellInfo.rowSpan = 1;
4285 aCellInfo.colSpan = 1;
4286 if (aCellData) {
4287 aCellInfo.cell = (nsBCTableCellFrame*)aCellData->GetCellFrame();
4288 if (aCellInfo.cell) {
4289 if (!aCellInfo.topRow) {
4290 aCellInfo.topRow = static_cast<nsTableRowFrame*>
4291 (aCellInfo.cell->GetParent());
4292 if (!aCellInfo.topRow) ABORT0();
4293 aCellInfo.rowIndex = aCellInfo.topRow->GetRowIndex();
4295 aCellInfo.colSpan = mTableFrame.GetEffectiveColSpan(*aCellInfo.cell, aCellMap);
4296 aCellInfo.rowSpan = mTableFrame.GetEffectiveRowSpan(*aCellInfo.cell, aCellMap);
4299 if (!aCellInfo.topRow) {
4300 aCellInfo.topRow = mRow;
4303 if (1 == aCellInfo.rowSpan) {
4304 aCellInfo.bottomRow = aCellInfo.topRow;
4306 else {
4307 aCellInfo.bottomRow = aCellInfo.topRow->GetNextRow();
4308 if (aCellInfo.bottomRow) {
4309 for (PRInt32 spanX = 2; aCellInfo.bottomRow && (spanX < aCellInfo.rowSpan); spanX++) {
4310 aCellInfo.bottomRow = aCellInfo.bottomRow->GetNextRow();
4312 NS_ASSERTION(aCellInfo.bottomRow, "program error");
4314 else {
4315 NS_ASSERTION(PR_FALSE, "error in cell map");
4316 aCellInfo.rowSpan = 1;
4317 aCellInfo.bottomRow = aCellInfo.topRow;
4321 // row group frame info
4322 PRUint32 rgStart = mRowGroupStart;
4323 PRUint32 rgEnd = mRowGroupEnd;
4324 aCellInfo.rg = mTableFrame.GetRowGroupFrame(aCellInfo.topRow->GetParent());
4325 if (aCellInfo.rg != mRowGroup) {
4326 rgStart = aCellInfo.rg->GetStartRowIndex();
4327 rgEnd = rgStart + aCellInfo.rg->GetRowCount() - 1;
4329 PRUint32 rowIndex = aCellInfo.topRow->GetRowIndex();
4330 aCellInfo.rgTop = (rgStart == rowIndex);
4331 aCellInfo.rgBottom = (rgEnd == rowIndex + aCellInfo.rowSpan - 1);
4333 // col frame info
4334 aCellInfo.leftCol = mTableFrame.GetColFrame(aColIndex); if (!aCellInfo.leftCol) ABORT0();
4336 aCellInfo.rightCol = aCellInfo.leftCol;
4337 if (aCellInfo.colSpan > 1) {
4338 for (PRInt32 spanX = 1; spanX < aCellInfo.colSpan; spanX++) {
4339 nsTableColFrame* colFrame = mTableFrame.GetColFrame(aColIndex + spanX); if (!colFrame) ABORT0();
4340 aCellInfo.rightCol = colFrame;
4344 // col group frame info
4345 aCellInfo.cg = static_cast<nsTableColGroupFrame*>
4346 (aCellInfo.leftCol->GetParent());
4347 PRInt32 cgStart = aCellInfo.cg->GetStartColumnIndex();
4348 PRInt32 cgEnd = PR_MAX(0, cgStart + aCellInfo.cg->GetColCount() - 1);
4349 aCellInfo.cgLeft = (cgStart == aColIndex);
4350 aCellInfo.cgRight = (cgEnd == aColIndex + (PRInt32)aCellInfo.colSpan - 1);
4353 PRBool
4354 BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
4356 mAtEnd = PR_TRUE;
4357 mPrevRow = mRow;
4358 if (aRow) {
4359 mRow = aRow;
4361 else if (mRow) {
4362 mRow = mRow->GetNextRow();
4364 if (mRow) {
4365 mRowIndex = mRow->GetRowIndex();
4366 // get to the first entry with an originating cell
4367 PRInt32 rgRowIndex = mRowIndex - mRowGroupStart;
4368 if (PRUint32(rgRowIndex) >= mCellMap->mRows.Length())
4369 ABORT1(PR_FALSE);
4370 const nsCellMap::CellDataArray& row = mCellMap->mRows[rgRowIndex];
4372 for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
4373 CellData* cellData = row.SafeElementAt(mColIndex);
4374 if (!cellData) { // add a dead cell data
4375 nsRect damageArea;
4376 cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT1(PR_FALSE);
4378 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4379 break;
4382 mIsNewRow = PR_TRUE;
4383 mAtEnd = PR_FALSE;
4385 else ABORT1(PR_FALSE);
4387 return !mAtEnd;
4390 PRBool
4391 BCMapCellIterator::SetNewRowGroup(PRBool aFindFirstDamagedRow)
4393 mAtEnd = PR_TRUE;
4394 PRInt32 numRowGroups = mRowGroups.Length();
4395 mCellMap = nsnull;
4396 for (mRowGroupIndex++; mRowGroupIndex < numRowGroups; mRowGroupIndex++) {
4397 mRowGroup = mRowGroups[mRowGroupIndex];
4398 PRInt32 rowCount = mRowGroup->GetRowCount();
4399 mRowGroupStart = mRowGroup->GetStartRowIndex();
4400 mRowGroupEnd = mRowGroupStart + rowCount - 1;
4401 if (rowCount > 0) {
4402 mCellMap = mTableCellMap->GetMapFor(mRowGroup, mCellMap);
4403 if (!mCellMap) ABORT1(PR_FALSE);
4404 nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
4405 if (aFindFirstDamagedRow) {
4406 if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
4407 // the damage area starts in the row group
4408 if (aFindFirstDamagedRow) {
4409 // find the correct first damaged row
4410 PRInt32 numRows = mAreaStart.y - mRowGroupStart;
4411 for (PRInt32 i = 0; i < numRows; i++) {
4412 firstRow = firstRow->GetNextRow();
4413 if (!firstRow) ABORT1(PR_FALSE);
4417 else {
4418 continue;
4421 if (SetNewRow(firstRow)) { // sets mAtEnd
4422 break;
4427 return !mAtEnd;
4430 void
4431 BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
4433 aMapInfo.Reset();
4435 SetNewRowGroup(PR_TRUE); // sets mAtEnd
4436 while (!mAtEnd) {
4437 if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
4438 CellData* cellData = mCellMap->GetDataAt(mAreaStart.y - mRowGroupStart,
4439 mAreaStart.x);
4440 if (cellData && cellData->IsOrig()) {
4441 SetInfo(mRow, mAreaStart.x, cellData, aMapInfo);
4443 else {
4444 NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) , "damage area expanded incorrectly");
4445 mAtEnd = PR_TRUE;
4447 break;
4449 SetNewRowGroup(PR_TRUE); // sets mAtEnd
4453 void
4454 BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
4456 if (mAtEnd) ABORT0();
4457 aMapInfo.Reset();
4459 mIsNewRow = PR_FALSE;
4460 mColIndex++;
4461 while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
4462 for (; mColIndex <= mAreaEnd.x; mColIndex++) {
4463 PRInt32 rgRowIndex = mRowIndex - mRowGroupStart;
4464 CellData* cellData = mCellMap->GetDataAt(rgRowIndex, mColIndex);
4465 if (!cellData) { // add a dead cell data
4466 nsRect damageArea;
4467 cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0();
4469 if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
4470 SetInfo(mRow, mColIndex, cellData, aMapInfo);
4471 return;
4474 if (mRowIndex >= mRowGroupEnd) {
4475 SetNewRowGroup(PR_FALSE); // could set mAtEnd
4477 else {
4478 SetNewRow(); // could set mAtEnd
4481 mAtEnd = PR_TRUE;
4484 void
4485 BCMapCellIterator::PeekRight(BCMapCellInfo& aRefInfo,
4486 PRUint32 aRowIndex,
4487 BCMapCellInfo& aAjaInfo)
4489 aAjaInfo.Reset();
4490 PRInt32 colIndex = aRefInfo.colIndex + aRefInfo.colSpan;
4491 PRUint32 rgRowIndex = aRowIndex - mRowGroupStart;
4493 CellData* cellData = mCellMap->GetDataAt(rgRowIndex, colIndex);
4494 if (!cellData) { // add a dead cell data
4495 NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
4496 nsRect damageArea;
4497 cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0();
4499 nsTableRowFrame* row = nsnull;
4500 if (cellData->IsRowSpan()) {
4501 rgRowIndex -= cellData->GetRowSpanOffset();
4502 cellData = mCellMap->GetDataAt(rgRowIndex, colIndex);
4503 if (!cellData)
4504 ABORT0();
4506 else {
4507 row = mRow;
4509 SetInfo(row, colIndex, cellData, aAjaInfo);
4512 void
4513 BCMapCellIterator::PeekBottom(BCMapCellInfo& aRefInfo,
4514 PRUint32 aColIndex,
4515 BCMapCellInfo& aAjaInfo)
4517 aAjaInfo.Reset();
4518 PRInt32 rowIndex = aRefInfo.rowIndex + aRefInfo.rowSpan;
4519 PRInt32 rgRowIndex = rowIndex - mRowGroupStart;
4520 nsTableRowGroupFrame* rg = mRowGroup;
4521 nsCellMap* cellMap = mCellMap;
4522 nsTableRowFrame* nextRow = nsnull;
4523 if (rowIndex > mRowGroupEnd) {
4524 PRInt32 nextRgIndex = mRowGroupIndex;
4525 do {
4526 nextRgIndex++;
4527 rg = mRowGroups.SafeElementAt(nextRgIndex);
4528 if (rg) {
4529 cellMap = mTableCellMap->GetMapFor(rg, cellMap); if (!cellMap) ABORT0();
4530 rgRowIndex = 0;
4531 nextRow = rg->GetFirstRow();
4534 while (rg && !nextRow);
4535 if(!rg) return;
4537 else {
4538 // get the row within the same row group
4539 nextRow = mRow;
4540 for (PRInt32 i = 0; i < aRefInfo.rowSpan; i++) {
4541 nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
4545 CellData* cellData = cellMap->GetDataAt(rgRowIndex, aColIndex);
4546 if (!cellData) { // add a dead cell data
4547 NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
4548 nsRect damageArea;
4549 cellData = cellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0();
4551 if (cellData->IsColSpan()) {
4552 aColIndex -= cellData->GetColSpanOffset();
4553 cellData = cellMap->GetDataAt(rgRowIndex, aColIndex);
4555 SetInfo(nextRow, aColIndex, cellData, aAjaInfo, cellMap);
4558 // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
4559 // will return the priority of NS_STYLE_BORDER_STYLE_SOLID.
4560 static PRUint8 styleToPriority[13] = { 0, // NS_STYLE_BORDER_STYLE_NONE
4561 2, // NS_STYLE_BORDER_STYLE_GROOVE
4562 4, // NS_STYLE_BORDER_STYLE_RIDGE
4563 5, // NS_STYLE_BORDER_STYLE_DOTTED
4564 6, // NS_STYLE_BORDER_STYLE_DASHED
4565 7, // NS_STYLE_BORDER_STYLE_SOLID
4566 8, // NS_STYLE_BORDER_STYLE_DOUBLE
4567 1, // NS_STYLE_BORDER_STYLE_INSET
4568 3, // NS_STYLE_BORDER_STYLE_OUTSET
4569 9 };// NS_STYLE_BORDER_STYLE_HIDDEN
4570 // priority rules follow CSS 2.1 spec
4571 // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
4572 // and the lowest: 'inset'. none is even weaker
4573 #define CELL_CORNER PR_TRUE
4575 /** return the border style, border color for a given frame and side
4576 * @param aFrame - query the info for this frame
4577 * @param aSide - the side of the frame
4578 * @param aStyle - the border style
4579 * @param aColor - the border color
4580 * @param aTableIsLTR - table direction is LTR
4581 * @param aIgnoreTableEdge - if is a table edge any borders set for the purpose
4582 * of satisfying the rules attribute should be ignored
4584 static void
4585 GetColorAndStyle(const nsIFrame* aFrame,
4586 PRUint8 aSide,
4587 PRUint8& aStyle,
4588 nscolor& aColor,
4589 PRBool aTableIsLTR,
4590 PRBool aIgnoreTableEdge)
4592 NS_PRECONDITION(aFrame, "null frame");
4593 // initialize out arg
4594 aColor = 0;
4595 const nsStyleBorder* styleData = aFrame->GetStyleBorder();
4596 if(!aTableIsLTR) { // revert the directions
4597 if (NS_SIDE_RIGHT == aSide) {
4598 aSide = NS_SIDE_LEFT;
4600 else if (NS_SIDE_LEFT == aSide) {
4601 aSide = NS_SIDE_RIGHT;
4604 aStyle = styleData->GetBorderStyle(aSide);
4606 // if the rules marker is set, set the style either to none or remove the mask
4607 if (NS_STYLE_BORDER_STYLE_RULES_MARKER & aStyle) {
4608 if (aIgnoreTableEdge) {
4609 aStyle = NS_STYLE_BORDER_STYLE_NONE;
4610 return;
4612 else {
4613 aStyle &= ~NS_STYLE_BORDER_STYLE_RULES_MARKER;
4617 if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
4618 (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
4619 return;
4621 PRBool foreground;
4622 styleData->GetBorderColor(aSide, aColor, foreground);
4623 if (foreground) {
4624 aColor = aFrame->GetStyleColor()->mColor;
4628 /** coerce the paint style as required by CSS2.1
4629 * @param aFrame - query the info for this frame
4630 * @param aSide - the side of the frame
4631 * @param aStyle - the border style
4632 * @param aColor - the border color
4633 * @param aTableIsLTR - table direction is LTR
4634 * @param aIgnoreTableEdge - if is a table edge any borders set for the purpose
4635 * of satisfying the rules attribute should be ignored
4637 static void
4638 GetPaintStyleInfo(const nsIFrame* aFrame,
4639 PRUint8 aSide,
4640 PRUint8& aStyle,
4641 nscolor& aColor,
4642 PRBool aTableIsLTR,
4643 PRBool aIgnoreTableEdge)
4645 GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR, aIgnoreTableEdge);
4646 if (NS_STYLE_BORDER_STYLE_INSET == aStyle) {
4647 aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
4649 else if (NS_STYLE_BORDER_STYLE_OUTSET == aStyle) {
4650 aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
4654 /** return the border style, border color and the width in pixel for a given
4655 * frame and side
4656 * @param aFrame - query the info for this frame
4657 * @param aSide - the side of the frame
4658 * @param aStyle - the border style
4659 * @param aColor - the border color
4660 * @param aTableIsLTR - table direction is LTR
4661 * @param aIgnoreTableEdge - if is a table edge any borders set for the purpose
4662 * of satisfying the rules attribute should be ignored
4663 * @param aWidth - the border width in px.
4664 * @param aTwipsToPixels - conversion factor from twips to pixel
4666 static void
4667 GetColorAndStyle(const nsIFrame* aFrame,
4668 PRUint8 aSide,
4669 PRUint8& aStyle,
4670 nscolor& aColor,
4671 PRBool aTableIsLTR,
4672 PRBool aIgnoreTableEdge,
4673 nscoord& aWidth)
4675 GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR, aIgnoreTableEdge);
4676 if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
4677 (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
4678 aWidth = 0;
4679 return;
4681 const nsStyleBorder* styleData = aFrame->GetStyleBorder();
4682 nscoord width;
4683 if(!aTableIsLTR) { // revert the directions
4684 if (NS_SIDE_RIGHT == aSide) {
4685 aSide = NS_SIDE_LEFT;
4687 else if (NS_SIDE_LEFT == aSide) {
4688 aSide = NS_SIDE_RIGHT;
4691 width = styleData->GetActualBorderWidth(aSide);
4692 aWidth = nsPresContext::AppUnitsToIntCSSPixels(width);
4695 class nsDelayedCalcBCBorders : public nsRunnable {
4696 public:
4697 nsDelayedCalcBCBorders(nsIFrame* aFrame) :
4698 mFrame(aFrame) {}
4700 NS_IMETHOD Run() {
4701 if (mFrame) {
4702 nsTableFrame* tableFrame = static_cast <nsTableFrame*>(mFrame.GetFrame());
4703 if (tableFrame->NeedToCalcBCBorders()) {
4704 tableFrame->CalcBCBorders();
4707 return NS_OK;
4709 private:
4710 nsWeakFrame mFrame;
4713 PRBool
4714 nsTableFrame::BCRecalcNeeded(nsStyleContext* aOldStyleContext,
4715 nsStyleContext* aNewStyleContext)
4717 // Attention: the old style context is the one we're forgetting,
4718 // and hence possibly completely bogus for GetStyle* purposes.
4719 // We use PeekStyleData instead.
4721 const nsStyleBorder* oldStyleData = static_cast<const nsStyleBorder*>
4722 (aOldStyleContext->PeekStyleData(eStyleStruct_Border));
4723 if (!oldStyleData)
4724 return PR_FALSE;
4726 const nsStyleBorder* newStyleData = aNewStyleContext->GetStyleBorder();
4727 nsChangeHint change = newStyleData->CalcDifference(*oldStyleData);
4728 if (!change)
4729 return PR_FALSE;
4730 if (change & nsChangeHint_ReflowFrame)
4731 return PR_TRUE; // the caller only needs to mark the bc damage area
4732 if (change & nsChangeHint_RepaintFrame) {
4733 // we need to recompute the borders and the caller needs to mark
4734 // the bc damage area
4735 // XXX In principle this should only be necessary for border style changes
4736 // However the bc painting code tries to maximize the drawn border segments
4737 // so it stores in the cellmap where a new border segment starts and this
4738 // introduces a unwanted cellmap data dependence on color
4739 nsCOMPtr<nsIRunnable> evt = new nsDelayedCalcBCBorders(this);
4740 NS_DispatchToCurrentThread(evt);
4741 return PR_TRUE;
4743 return PR_FALSE;
4746 /* BCCellBorder represents a border segment which can be either a horizontal
4747 * or a vertical segment. For each segment we need to know the color, width,
4748 * style, who owns it and how long it is in cellmap coordinates.
4749 * Ownership of these segments is important to calculate which corners should
4750 * be bevelled. This structure has dual use, its used first to compute the
4751 * dominant border for horizontal and vertical segments and to store the
4752 * preliminary computed border results in the BCCellBorders structure.
4753 * This temporary storage is not symmetric with respect to horizontal and
4754 * vertical border segments, its always column oriented. For each column in
4755 * the cellmap there is a temporary stored vertical and horizontal segment.
4756 * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
4758 struct BCCellBorder
4760 BCCellBorder() { Reset(0, 1); }
4761 void Reset(PRUint32 aRowIndex, PRUint32 aRowSpan);
4762 nscolor color; // border segment color
4763 nscoord width; // border segment width in pixel coordinates !!
4764 PRUint8 style; // border segment style, possible values are defined
4765 // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
4766 BCBorderOwner owner; // border segment owner, possible values are defined
4767 // in celldata.h. In the cellmap for each border
4768 // segment we store the owner and later when
4769 // painting we know the owner and can retrieve the
4770 // style info from the corresponding frame
4771 PRInt32 rowIndex; // rowIndex of temporary stored horizontal border segments
4772 PRInt32 rowSpan; // row span of temporary stored horizontal border segments
4775 void
4776 BCCellBorder::Reset(PRUint32 aRowIndex,
4777 PRUint32 aRowSpan)
4779 style = NS_STYLE_BORDER_STYLE_NONE;
4780 color = 0;
4781 width = 0;
4782 owner = eTableOwner;
4783 rowIndex = aRowIndex;
4784 rowSpan = aRowSpan;
4787 // Compare two border segments, this comparison depends whether the two
4788 // segments meet at a corner and whether the second segment is horizontal.
4789 // The return value is whichever of aBorder1 or aBorder2 dominates.
4790 static const BCCellBorder&
4791 CompareBorders(PRBool aIsCorner, // Pass PR_TRUE for corner calculations
4792 const BCCellBorder& aBorder1,
4793 const BCCellBorder& aBorder2,
4794 PRBool aSecondIsHorizontal,
4795 PRBool* aFirstDominates = nsnull)
4797 PRBool firstDominates = PR_TRUE;
4799 if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
4800 firstDominates = (aIsCorner) ? PR_FALSE : PR_TRUE;
4802 else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
4803 firstDominates = (aIsCorner) ? PR_TRUE : PR_FALSE;
4805 else if (aBorder1.width < aBorder2.width) {
4806 firstDominates = PR_FALSE;
4808 else if (aBorder1.width == aBorder2.width) {
4809 if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
4810 firstDominates = PR_FALSE;
4812 else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
4813 if (aBorder1.owner == aBorder2.owner) {
4814 firstDominates = !aSecondIsHorizontal;
4816 else if (aBorder1.owner < aBorder2.owner) {
4817 firstDominates = PR_FALSE;
4822 if (aFirstDominates)
4823 *aFirstDominates = firstDominates;
4825 if (firstDominates)
4826 return aBorder1;
4827 return aBorder2;
4830 /** calc the dominant border by considering the table, row/col group, row/col,
4831 * cell. At the table edges borders coming from the 'rules' attribute should
4832 * be ignored as they are only inner borders.
4833 * Depending on whether the side is vertical or horizontal and whether
4834 * adjacent frames are taken into account the ownership of a single border
4835 * segment is defined. The return value is the dominating border
4836 * The cellmap stores only top and left borders for each cellmap position.
4837 * If the cell border is owned by the cell that is left of the border
4838 * it will be an adjacent owner aka eAjaCellOwner. See celldata.h for the other
4839 * scenarios with a adjacent owner.
4840 * @param xxxFrame - the frame for style information, might be zero if
4841 * it should not be considered
4842 * @param aIgnoreTableEdge - if true the border should be ignored at the table
4843 * edge, as rules can be drawn only inside the table
4844 * @param aSide - side of the frames that should be considered
4845 * @param aAja - the border comparison takes place from the point of
4846 * a frame that is adjacent to the cellmap entry, for
4847 * when a cell owns its lower border it will be the
4848 * adjacent owner as in the cellmap only top and left
4849 * borders are stored.
4850 * @param aTwipsToPixels - conversion factor as borders need to be drawn pixel
4851 * aligned.
4853 static BCCellBorder
4854 CompareBorders(const nsIFrame* aTableFrame,
4855 const nsIFrame* aColGroupFrame,
4856 const nsIFrame* aColFrame,
4857 const nsIFrame* aRowGroupFrame,
4858 const nsIFrame* aRowFrame,
4859 const nsIFrame* aCellFrame,
4860 PRBool aTableIsLTR,
4861 PRBool aIgnoreTableEdge,
4862 PRUint8 aSide,
4863 PRBool aAja)
4865 BCCellBorder border, tempBorder;
4866 PRBool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide);
4868 // start with the table as dominant if present
4869 if (aTableFrame) {
4870 GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, aIgnoreTableEdge, border.width);
4871 border.owner = eTableOwner;
4872 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4873 return border;
4876 // see if the colgroup is dominant
4877 if (aColGroupFrame) {
4878 GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width);
4879 tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner;
4880 // pass here and below PR_FALSE for aSecondIsHorizontal as it is only used for corner calculations.
4881 border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
4882 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4883 return border;
4886 // see if the col is dominant
4887 if (aColFrame) {
4888 GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width);
4889 tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner;
4890 border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
4891 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4892 return border;
4895 // see if the rowgroup is dominant
4896 if (aRowGroupFrame) {
4897 GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width);
4898 tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner;
4899 border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
4900 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4901 return border;
4904 // see if the row is dominant
4905 if (aRowFrame) {
4906 GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width);
4907 tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner;
4908 border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
4909 if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
4910 return border;
4913 // see if the cell is dominant
4914 if (aCellFrame) {
4915 GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width);
4916 tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner;
4917 border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
4919 return border;
4922 static PRBool
4923 Perpendicular(PRUint8 aSide1,
4924 PRUint8 aSide2)
4926 switch (aSide1) {
4927 case NS_SIDE_TOP:
4928 return (NS_SIDE_BOTTOM != aSide2);
4929 case NS_SIDE_RIGHT:
4930 return (NS_SIDE_LEFT != aSide2);
4931 case NS_SIDE_BOTTOM:
4932 return (NS_SIDE_TOP != aSide2);
4933 default: // NS_SIDE_LEFT
4934 return (NS_SIDE_RIGHT != aSide2);
4938 // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
4939 struct BCCornerInfo
4941 BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerSide = ownerElem = subSide =
4942 subElem = hasDashDot = numSegs = bevel = 0;
4943 ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID; }
4944 void Set(PRUint8 aSide,
4945 BCCellBorder border);
4947 void Update(PRUint8 aSide,
4948 BCCellBorder border);
4950 nscolor ownerColor; // color of borderOwner
4951 PRUint16 ownerWidth; // pixel width of borderOwner
4952 PRUint16 subWidth; // pixel width of the largest border intersecting the border perpendicular
4953 // to ownerSide
4954 PRUint32 ownerSide:2; // side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border owning
4955 // the corner relative to the corner
4956 PRUint32 ownerElem:3; // elem type (e.g. eTable, eGroup, etc) owning the corner
4957 PRUint32 ownerStyle:8; // border style of ownerElem
4958 PRUint32 subSide:2; // side of border with subWidth relative to the corner
4959 PRUint32 subElem:3; // elem type (e.g. eTable, eGroup, etc) of sub owner
4960 PRUint32 subStyle:8; // border style of subElem
4961 PRUint32 hasDashDot:1; // does a dashed, dotted segment enter the corner, they cannot be beveled
4962 PRUint32 numSegs:3; // number of segments entering corner
4963 PRUint32 bevel:1; // is the corner beveled (uses the above two fields together with subWidth)
4964 PRUint32 unused:1;
4967 void
4968 BCCornerInfo::Set(PRUint8 aSide,
4969 BCCellBorder aBorder)
4971 ownerElem = aBorder.owner;
4972 ownerStyle = aBorder.style;
4973 ownerWidth = aBorder.width;
4974 ownerColor = aBorder.color;
4975 ownerSide = aSide;
4976 hasDashDot = 0;
4977 numSegs = 0;
4978 if (aBorder.width > 0) {
4979 numSegs++;
4980 hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
4981 (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
4983 bevel = 0;
4984 subWidth = 0;
4985 // the following will get set later
4986 subSide = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT;
4987 subElem = eTableOwner;
4988 subStyle = NS_STYLE_BORDER_STYLE_SOLID;
4991 void
4992 BCCornerInfo::Update(PRUint8 aSide,
4993 BCCellBorder aBorder)
4995 PRBool existingWins = PR_FALSE;
4996 if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
4997 Set(aSide, aBorder);
4999 else {
5000 PRBool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner
5001 BCCellBorder oldBorder, tempBorder;
5002 oldBorder.owner = (BCBorderOwner) ownerElem;
5003 oldBorder.style = ownerStyle;
5004 oldBorder.width = ownerWidth;
5005 oldBorder.color = ownerColor;
5007 PRUint8 oldSide = ownerSide;
5009 tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins);
5011 ownerElem = tempBorder.owner;
5012 ownerStyle = tempBorder.style;
5013 ownerWidth = tempBorder.width;
5014 ownerColor = tempBorder.color;
5015 if (existingWins) { // existing corner is dominant
5016 if (::Perpendicular(ownerSide, aSide)) {
5017 // see if the new sub info replaces the old
5018 BCCellBorder subBorder;
5019 subBorder.owner = (BCBorderOwner) subElem;
5020 subBorder.style = subStyle;
5021 subBorder.width = subWidth;
5022 subBorder.color = 0; // we are not interested in subBorder color
5023 PRBool firstWins;
5025 tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins);
5027 subElem = tempBorder.owner;
5028 subStyle = tempBorder.style;
5029 subWidth = tempBorder.width;
5030 if (!firstWins) {
5031 subSide = aSide;
5035 else { // input args are dominant
5036 ownerSide = aSide;
5037 if (::Perpendicular(oldSide, ownerSide)) {
5038 subElem = oldBorder.owner;
5039 subStyle = oldBorder.style;
5040 subWidth = oldBorder.width;
5041 subSide = oldSide;
5044 if (aBorder.width > 0) {
5045 numSegs++;
5046 if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
5047 (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
5048 hasDashDot = 1;
5052 // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
5053 bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
5057 struct BCCorners
5059 BCCorners(PRInt32 aNumCorners,
5060 PRInt32 aStartIndex);
5062 ~BCCorners() { delete [] corners; }
5064 BCCornerInfo& operator [](PRInt32 i) const
5065 { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
5066 return corners[PR_MAX(PR_MIN(i, endIndex), startIndex) - startIndex]; }
5068 PRInt32 startIndex;
5069 PRInt32 endIndex;
5070 BCCornerInfo* corners;
5073 BCCorners::BCCorners(PRInt32 aNumCorners,
5074 PRInt32 aStartIndex)
5076 NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
5077 startIndex = aStartIndex;
5078 endIndex = aStartIndex + aNumCorners - 1;
5079 corners = new BCCornerInfo[aNumCorners];
5083 struct BCCellBorders
5085 BCCellBorders(PRInt32 aNumBorders,
5086 PRInt32 aStartIndex);
5088 ~BCCellBorders() { delete [] borders; }
5090 BCCellBorder& operator [](PRInt32 i) const
5091 { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
5092 return borders[PR_MAX(PR_MIN(i, endIndex), startIndex) - startIndex]; }
5094 PRInt32 startIndex;
5095 PRInt32 endIndex;
5096 BCCellBorder* borders;
5099 BCCellBorders::BCCellBorders(PRInt32 aNumBorders,
5100 PRInt32 aStartIndex)
5102 NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
5103 startIndex = aStartIndex;
5104 endIndex = aStartIndex + aNumBorders - 1;
5105 borders = new BCCellBorder[aNumBorders];
5108 // this function sets the new border properties and returns true if the border
5109 // segment will start a new segment and not prolong the existing segment.
5110 static PRBool
5111 SetBorder(const BCCellBorder& aNewBorder,
5112 BCCellBorder& aBorder)
5114 PRBool changed = (aNewBorder.style != aBorder.style) ||
5115 (aNewBorder.width != aBorder.width) ||
5116 (aNewBorder.color != aBorder.color);
5117 aBorder.color = aNewBorder.color;
5118 aBorder.width = aNewBorder.width;
5119 aBorder.style = aNewBorder.style;
5120 aBorder.owner = aNewBorder.owner;
5122 return changed;
5125 // this function will set the horizontal border. It will return true if the
5126 // existing segment will not be continued. Having a vertical owner of a corner
5127 // should also start a new segment.
5128 static PRBool
5129 SetHorBorder(const BCCellBorder& aNewBorder,
5130 const BCCornerInfo& aCorner,
5131 BCCellBorder& aBorder)
5133 PRBool startSeg = ::SetBorder(aNewBorder, aBorder);
5134 if (!startSeg) {
5135 startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide));
5137 return startSeg;
5140 // Make the damage area larger on the top and bottom by at least one row and on the left and right
5141 // at least one column. This is done so that adjacent elements are part of the border calculations.
5142 // The extra segments and borders outside the actual damage area will not be updated in the cell map,
5143 // because they in turn would need info from adjacent segments outside the damage area to be accurate.
5144 void
5145 nsTableFrame::ExpandBCDamageArea(nsRect& aRect) const
5147 PRInt32 numRows = GetRowCount();
5148 PRInt32 numCols = GetColCount();
5150 PRInt32 dStartX = aRect.x;
5151 PRInt32 dEndX = aRect.XMost() - 1;
5152 PRInt32 dStartY = aRect.y;
5153 PRInt32 dEndY = aRect.YMost() - 1;
5155 // expand the damage area in each direction
5156 if (dStartX > 0) {
5157 dStartX--;
5159 if (dEndX < (numCols - 1)) {
5160 dEndX++;
5162 if (dStartY > 0) {
5163 dStartY--;
5165 if (dEndY < (numRows - 1)) {
5166 dEndY++;
5168 // Check the damage area so that there are no cells spanning in or out. If there are any then
5169 // make the damage area as big as the table, similarly to the way the cell map decides whether
5170 // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
5171 // no spanners, but it may not be worth the effort in general, and it would need to be done in the
5172 // cell map as well.
5173 PRBool haveSpanner = PR_FALSE;
5174 if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
5175 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
5176 // Get the ordered row groups
5177 RowGroupArray rowGroups;
5178 OrderRowGroups(rowGroups);
5180 // Scope outside loop to be used as hint.
5181 nsCellMap* cellMap = nsnull;
5182 for (PRUint32 rgX = 0; rgX < rowGroups.Length(); rgX++) {
5183 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
5184 PRInt32 rgStartY = rgFrame->GetStartRowIndex();
5185 PRInt32 rgEndY = rgStartY + rgFrame->GetRowCount() - 1;
5186 if (dEndY < rgStartY)
5187 break;
5188 cellMap = tableCellMap->GetMapFor(rgFrame, cellMap);
5189 if (!cellMap) ABORT0();
5190 // check for spanners from above and below
5191 if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
5192 if (PRUint32(dStartY - rgStartY) >= cellMap->mRows.Length())
5193 ABORT0();
5194 const nsCellMap::CellDataArray& row =
5195 cellMap->mRows[dStartY - rgStartY];
5196 for (PRInt32 x = dStartX; x <= dEndX; x++) {
5197 CellData* cellData = row.SafeElementAt(x);
5198 if (cellData && (cellData->IsRowSpan())) {
5199 haveSpanner = PR_TRUE;
5200 break;
5203 if (dEndY < rgEndY) {
5204 if (PRUint32(dEndY + 1 - rgStartY) >= cellMap->mRows.Length())
5205 ABORT0();
5206 const nsCellMap::CellDataArray& row2 =
5207 cellMap->mRows[dEndY + 1 - rgStartY];
5208 for (PRInt32 x = dStartX; x <= dEndX; x++) {
5209 CellData* cellData = row2.SafeElementAt(x);
5210 if (cellData && (cellData->IsRowSpan())) {
5211 haveSpanner = PR_TRUE;
5212 break;
5217 // check for spanners on the left and right
5218 PRInt32 iterStartY = -1;
5219 PRInt32 iterEndY = -1;
5220 if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
5221 // the damage area starts in the row group
5222 iterStartY = dStartY;
5223 iterEndY = PR_MIN(dEndY, rgEndY);
5225 else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
5226 // the damage area ends in the row group
5227 iterStartY = rgStartY;
5228 iterEndY = PR_MIN(dEndY, rgStartY);
5230 else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
5231 // the damage area contains the row group
5232 iterStartY = rgStartY;
5233 iterEndY = rgEndY;
5235 if ((iterStartY >= 0) && (iterEndY >= 0)) {
5236 for (PRInt32 y = iterStartY; y <= iterEndY; y++) {
5237 if (PRUint32(y - rgStartY) >= cellMap->mRows.Length())
5238 ABORT0();
5239 const nsCellMap::CellDataArray& row =
5240 cellMap->mRows[y - rgStartY];
5241 CellData* cellData = row.SafeElementAt(dStartX);
5242 if (cellData && (cellData->IsColSpan())) {
5243 haveSpanner = PR_TRUE;
5244 break;
5246 if (dEndX < (numCols - 1)) {
5247 cellData = row.SafeElementAt(dEndX + 1);
5248 if (cellData && (cellData->IsColSpan())) {
5249 haveSpanner = PR_TRUE;
5250 break;
5257 if (haveSpanner) {
5258 // make the damage area the whole table
5259 aRect.x = 0;
5260 aRect.y = 0;
5261 aRect.width = numCols;
5262 aRect.height = numRows;
5264 else {
5265 aRect.x = dStartX;
5266 aRect.y = dStartY;
5267 aRect.width = 1 + dEndX - dStartX;
5268 aRect.height = 1 + dEndY - dStartY;
5272 #define MAX_TABLE_BORDER_WIDTH 255
5273 static PRUint8
5274 LimitBorderWidth(PRUint16 aWidth)
5276 return PR_MIN(MAX_TABLE_BORDER_WIDTH, aWidth);
5279 /* Here is the order for storing border edges in the cell map as a cell is processed. There are
5280 n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
5282 1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
5283 a bottom edge from a cell above will take care of it.
5284 2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
5285 a right edge from a cell to the left will take care of it.
5286 3) Store the right edge (or edges if a row span)
5287 4) Store the bottom edge (or edges if a col span)
5289 Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
5290 calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
5291 would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing
5292 borders already have about an extra 8 byte per cell map entry overhead (this could be
5293 reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the
5294 rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
5295 left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border
5296 edges per cell and n=rowspan left and right border edges per cell.
5298 1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
5299 Never store the top-right corner, since it will get stored as a right-top corner.
5300 2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
5301 since it will get stored as a bottom-left corner.
5302 3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
5303 the top edge of the table. Never store the right-bottom corner since it will get stored as a
5304 bottom-right corner.
5305 4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it
5306 otherwise, since it will get stored as either a right-top corner by a cell below or
5307 a bottom-left corner from a cell to the right.
5308 5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits
5309 the top side of a colspan in its interior. Never store the corner otherwise, since it will
5310 get stored as a right-top corner by a cell from below.
5312 XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
5313 In order to draw borders in rtl conditions somehow correct, the existing structure which relies
5314 heavily on the assumption that the next cell sibling will be on the right side, has been modified.
5315 We flip the border during painting and during style lookup. Look for tableIsLTR for places where
5316 the flipping is done.
5319 #define TABLE_EDGE PR_TRUE
5320 #define ADJACENT PR_TRUE
5321 #define HORIZONTAL PR_TRUE
5323 // Calc the dominant border at every cell edge and corner within the current damage area
5324 void
5325 nsTableFrame::CalcBCBorders()
5327 NS_ASSERTION(IsBorderCollapse(),
5328 "calling CalcBCBorders on separated-border table");
5329 nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
5330 PRInt32 numRows = GetRowCount();
5331 PRInt32 numCols = GetColCount();
5332 if (!numRows || !numCols)
5333 return; // nothing to do
5335 // Get the property holding the table damage area and border widths
5336 BCPropertyData* propData =
5337 (BCPropertyData*)nsTableFrame::GetProperty(this, nsGkAtoms::tableBCProperty, PR_FALSE);
5338 if (!propData) ABORT0();
5340 PRBool tableIsLTR = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
5341 PRUint8 firstSide, secondSide;
5342 if (tableIsLTR) {
5343 firstSide = NS_SIDE_LEFT;
5344 secondSide = NS_SIDE_RIGHT;
5346 else {
5347 firstSide = NS_SIDE_RIGHT;
5348 secondSide = NS_SIDE_LEFT;
5350 CheckFixDamageArea(numRows, numCols, propData->mDamageArea);
5351 // calculate an expanded damage area
5352 nsRect damageArea(propData->mDamageArea);
5353 ExpandBCDamageArea(damageArea);
5355 // segments that are on the table border edges need to be initialized only once
5356 PRBool tableBorderReset[4];
5357 for (PRUint32 sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) {
5358 tableBorderReset[sideX] = PR_FALSE;
5361 // vertical borders indexed in x-direction (cols)
5362 BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x); if (!lastVerBorders.borders) ABORT0();
5363 BCCellBorder lastTopBorder, lastBottomBorder;
5364 // horizontal borders indexed in x-direction (cols)
5365 BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x); if (!lastBottomBorders.borders) ABORT0();
5366 PRBool startSeg;
5367 PRBool gotRowBorder = PR_FALSE;
5369 BCMapCellInfo info, ajaInfo;
5370 BCCellBorder currentBorder, adjacentBorder;
5371 PRInt32 cellEndRowIndex = -1;
5372 PRInt32 cellEndColIndex = -1;
5373 BCCorners topCorners(damageArea.width + 1, damageArea.x); if (!topCorners.corners) ABORT0();
5374 BCCorners bottomCorners(damageArea.width + 1, damageArea.x); if (!bottomCorners.corners) ABORT0();
5376 BCMapCellIterator iter(*this, damageArea);
5377 for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
5379 cellEndRowIndex = info.rowIndex + info.rowSpan - 1;
5380 cellEndColIndex = info.colIndex + info.colSpan - 1;
5382 PRBool bottomRowSpan = PR_FALSE;
5383 // see if lastTopBorder, lastBottomBorder need to be reset
5384 if (iter.IsNewRow()) {
5385 gotRowBorder = PR_FALSE;
5386 lastTopBorder.Reset(info.rowIndex, info.rowSpan);
5387 lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan);
5389 else if (info.colIndex > damageArea.x) {
5390 lastBottomBorder = lastBottomBorders[info.colIndex - 1];
5391 if (info.rowIndex > lastBottomBorder.rowIndex - lastBottomBorder.rowSpan) {
5392 // the top border's left edge butts against the middle of a rowspan
5393 lastTopBorder.Reset(info.rowIndex, info.rowSpan);
5395 if (lastBottomBorder.rowIndex > (cellEndRowIndex + 1)) {
5396 // the bottom border's left edge butts against the middle of a rowspan
5397 lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan);
5398 bottomRowSpan = PR_TRUE;
5402 // find the dominant border considering the cell's top border and the table, row group, row
5403 // if the border is at the top of the table, otherwise it was processed in a previous row
5404 if (0 == info.rowIndex) {
5405 if (!tableBorderReset[NS_SIDE_TOP]) {
5406 propData->mTopBorderWidth = 0;
5407 tableBorderReset[NS_SIDE_TOP] = PR_TRUE;
5409 for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) {
5410 nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0();
5411 nsIFrame* cgFrame = colFrame->GetParent(); if (!cgFrame) ABORT0();
5412 currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg, info.topRow,
5413 info.cell, tableIsLTR, TABLE_EDGE, NS_SIDE_TOP,
5414 !ADJACENT);
5415 // update/store the top left & top right corners of the seg
5416 BCCornerInfo& tlCorner = topCorners[colX]; // top left
5417 if (0 == colX) {
5418 tlCorner.Set(NS_SIDE_RIGHT, currentBorder); // we are on right hand side of the corner
5420 else {
5421 tlCorner.Update(NS_SIDE_RIGHT, currentBorder);
5422 tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, 0, 0, colX,
5423 tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel);
5425 topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right
5426 // update lastTopBorder and see if a new segment starts
5427 startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder);
5428 // store the border segment in the cell map
5429 tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *info.cellMap, 0, 0, colX,
5430 1, currentBorder.owner, currentBorder.width, startSeg);
5431 // update the affected borders of the cell, row, and table
5432 if (info.cell) {
5433 info.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(NS_SIDE_TOP)));
5435 if (info.topRow) {
5436 BCPixelSize half = BC_BORDER_BOTTOM_HALF(currentBorder.width);
5437 info.topRow->SetTopBCBorderWidth(PR_MAX(half, info.topRow->GetTopBCBorderWidth()));
5439 propData->mTopBorderWidth = LimitBorderWidth(PR_MAX(propData->mTopBorderWidth, (PRUint8)currentBorder.width));
5440 //calculate column continuous borders
5441 //we only need to do this once, so we'll do it only on the first row
5442 currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg,
5443 info.topRow, nsnull, tableIsLTR,
5444 TABLE_EDGE, NS_SIDE_TOP, !ADJACENT);
5445 ((nsTableColFrame*)colFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP,
5446 currentBorder.width);
5447 if (numCols == cellEndColIndex + 1) {
5448 currentBorder = CompareBorders(this, cgFrame, colFrame, nsnull,
5449 nsnull, nsnull, tableIsLTR, TABLE_EDGE,
5450 NS_SIDE_RIGHT, !ADJACENT);
5452 else {
5453 currentBorder = CompareBorders(nsnull, cgFrame, colFrame, nsnull,
5454 nsnull, nsnull, tableIsLTR, !TABLE_EDGE,
5455 NS_SIDE_RIGHT, !ADJACENT);
5457 ((nsTableColFrame*)colFrame)->SetContinuousBCBorderWidth(NS_SIDE_RIGHT,
5458 currentBorder.width);
5461 //calculate continuous top first row & rowgroup border: special case
5462 //because it must include the table in the collapse
5463 if (info.topRow) {
5464 currentBorder = CompareBorders(this, nsnull, nsnull, info.rg,
5465 info.topRow, nsnull, tableIsLTR,
5466 TABLE_EDGE, NS_SIDE_TOP, !ADJACENT);
5467 info.topRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5469 if (info.cgRight && info.cg) {
5470 //calculate continuous top colgroup border once per colgroup
5471 currentBorder = CompareBorders(this, info.cg, nsnull, info.rg,
5472 info.topRow, nsnull, tableIsLTR,
5473 TABLE_EDGE, NS_SIDE_TOP, !ADJACENT);
5474 info.cg->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5476 if (0 == info.colIndex) {
5477 currentBorder = CompareBorders(this, info.cg, info.leftCol, nsnull,
5478 nsnull, nsnull, tableIsLTR, TABLE_EDGE,
5479 NS_SIDE_LEFT, !ADJACENT);
5480 mBits.mLeftContBCBorder = currentBorder.width;
5483 else {
5484 // see if the top border needs to be the start of a segment due to a vertical border owning the corner
5485 if (info.colIndex > 0) {
5486 BCData& data = ((BCCellData*)info.cellData)->mData;
5487 if (!data.IsTopStart()) {
5488 PRUint8 cornerSide;
5489 PRPackedBool bevel;
5490 data.GetCorner(cornerSide, bevel);
5491 if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) {
5492 data.SetTopStart(PR_TRUE);
5498 // find the dominant border considering the cell's left border and the table, col group, col
5499 // if the border is at the left of the table, otherwise it was processed in a previous col
5500 if (0 == info.colIndex) {
5501 if (!tableBorderReset[NS_SIDE_LEFT]) {
5502 propData->mLeftBorderWidth = 0;
5503 tableBorderReset[NS_SIDE_LEFT] = PR_TRUE;
5505 nsTableRowFrame* rowFrame = nsnull;
5506 for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) {
5507 rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow();
5508 currentBorder = CompareBorders(this, info.cg, info.leftCol, info.rg, rowFrame, info.cell,
5509 tableIsLTR, TABLE_EDGE, NS_SIDE_LEFT, !ADJACENT);
5510 BCCornerInfo& tlCorner = (0 == rowX) ? topCorners[0] : bottomCorners[0]; // top left
5511 tlCorner.Update(NS_SIDE_BOTTOM, currentBorder);
5512 tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, iter.mRowGroupStart, rowX,
5513 0, tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel);
5514 bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left
5515 // update lastVerBordersBorder and see if a new segment starts
5516 startSeg = SetBorder(currentBorder, lastVerBorders[0]);
5517 // store the border segment in the cell map
5518 tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *info.cellMap, iter.mRowGroupStart, rowX,
5519 info.colIndex, 1, currentBorder.owner, currentBorder.width, startSeg);
5520 // update the left border of the cell, col and table
5521 if (info.cell) {
5522 info.cell->SetBorderWidth(firstSide, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(firstSide)));
5524 if (info.leftCol) {
5525 BCPixelSize half = BC_BORDER_RIGHT_HALF(currentBorder.width);
5526 info.leftCol->SetLeftBorderWidth(PR_MAX(half, info.leftCol->GetLeftBorderWidth()));
5528 propData->mLeftBorderWidth = LimitBorderWidth(PR_MAX(propData->mLeftBorderWidth, currentBorder.width));
5529 //get row continuous borders
5530 if (rowFrame) {
5531 currentBorder = CompareBorders(this, info.cg, info.leftCol,
5532 info.rg, rowFrame, nsnull, tableIsLTR,
5533 TABLE_EDGE, NS_SIDE_LEFT, !ADJACENT);
5534 rowFrame->SetContinuousBCBorderWidth(firstSide, currentBorder.width);
5537 //get row group continuous borders
5538 if (info.rgBottom && info.rg) { //once per row group, so check for bottom
5539 currentBorder = CompareBorders(this, info.cg, info.leftCol, info.rg, nsnull,
5540 nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_LEFT,
5541 !ADJACENT);
5542 info.rg->SetContinuousBCBorderWidth(firstSide, currentBorder.width);
5546 // find the dominant border considering the cell's right border, adjacent cells and the table, row group, row
5547 if (numCols == cellEndColIndex + 1) { // touches right edge of table
5548 if (!tableBorderReset[NS_SIDE_RIGHT]) {
5549 propData->mRightBorderWidth = 0;
5550 tableBorderReset[NS_SIDE_RIGHT] = PR_TRUE;
5552 nsTableRowFrame* rowFrame = nsnull;
5553 for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) {
5554 rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow();
5555 currentBorder = CompareBorders(this, info.cg, info.rightCol, info.rg, rowFrame, info.cell,
5556 tableIsLTR, TABLE_EDGE, NS_SIDE_RIGHT, ADJACENT);
5557 // update/store the top right & bottom right corners
5558 BCCornerInfo& trCorner = (0 == rowX) ? topCorners[cellEndColIndex + 1] : bottomCorners[cellEndColIndex + 1];
5559 trCorner.Update(NS_SIDE_BOTTOM, currentBorder); // top right
5560 tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX,
5561 cellEndColIndex, trCorner.ownerSide, trCorner.subWidth, trCorner.bevel);
5562 BCCornerInfo& brCorner = bottomCorners[cellEndColIndex + 1];
5563 brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right
5564 tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rowX,
5565 cellEndColIndex, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel);
5566 // update lastVerBorders and see if a new segment starts
5567 startSeg = SetBorder(currentBorder, lastVerBorders[cellEndColIndex + 1]);
5568 // store the border segment in the cell map and update cellBorders
5569 tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX,
5570 cellEndColIndex, 1, currentBorder.owner, currentBorder.width, startSeg);
5571 // update the affected borders of the cell, col, and table
5572 if (info.cell) {
5573 info.cell->SetBorderWidth(secondSide, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(secondSide)));
5575 if (info.rightCol) {
5576 BCPixelSize half = BC_BORDER_LEFT_HALF(currentBorder.width);
5577 info.rightCol->SetRightBorderWidth(PR_MAX(half, info.rightCol->GetRightBorderWidth()));
5579 propData->mRightBorderWidth = LimitBorderWidth(PR_MAX(propData->mRightBorderWidth, currentBorder.width));
5580 //get row continuous borders
5581 if (rowFrame) {
5582 currentBorder = CompareBorders(this, info.cg, info.rightCol, info.rg,
5583 rowFrame, nsnull, tableIsLTR, TABLE_EDGE,
5584 NS_SIDE_RIGHT, ADJACENT);
5585 rowFrame->SetContinuousBCBorderWidth(secondSide, currentBorder.width);
5588 //get row group continuous borders
5589 if (info.rgBottom && info.rg) { //once per rg, so check for bottom
5590 currentBorder = CompareBorders(this, info.cg, info.rightCol, info.rg,
5591 nsnull, nsnull, tableIsLTR, TABLE_EDGE,
5592 NS_SIDE_RIGHT, ADJACENT);
5593 info.rg->SetContinuousBCBorderWidth(secondSide, currentBorder.width);
5596 else {
5597 PRInt32 segLength = 0;
5598 BCMapCellInfo priorAjaInfo;
5599 for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX += segLength) {
5600 iter.PeekRight(info, rowX, ajaInfo);
5601 const nsIFrame* cg = (info.cgRight) ? info.cg : nsnull;
5602 currentBorder = CompareBorders(nsnull, cg, info.rightCol, nsnull, nsnull, info.cell,
5603 tableIsLTR, !TABLE_EDGE, NS_SIDE_RIGHT, ADJACENT);
5604 cg = (ajaInfo.cgLeft) ? ajaInfo.cg : nsnull;
5605 adjacentBorder = CompareBorders(nsnull, cg, ajaInfo.leftCol, nsnull, nsnull, ajaInfo.cell,
5606 tableIsLTR, !TABLE_EDGE, NS_SIDE_LEFT, !ADJACENT);
5607 currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, !HORIZONTAL);
5609 segLength = PR_MAX(1, ajaInfo.rowIndex + ajaInfo.rowSpan - rowX);
5610 segLength = PR_MIN(segLength, info.rowIndex + info.rowSpan - rowX);
5612 // update lastVerBorders and see if a new segment starts
5613 startSeg = SetBorder(currentBorder, lastVerBorders[cellEndColIndex + 1]);
5614 // store the border segment in the cell map and update cellBorders
5615 if (cellEndColIndex < damageArea.XMost() && rowX >= damageArea.y && rowX < damageArea.YMost()) {
5616 tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX,
5617 cellEndColIndex, segLength, currentBorder.owner, currentBorder.width, startSeg);
5618 // update the borders of the cells and cols affected
5619 if (info.cell) {
5620 info.cell->SetBorderWidth(secondSide, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(secondSide)));
5622 if (info.rightCol) {
5623 BCPixelSize half = BC_BORDER_LEFT_HALF(currentBorder.width);
5624 info.rightCol->SetRightBorderWidth(PR_MAX(half, info.rightCol->GetRightBorderWidth()));
5626 if (ajaInfo.cell) {
5627 ajaInfo.cell->SetBorderWidth(firstSide, PR_MAX(currentBorder.width, ajaInfo.cell->GetBorderWidth(firstSide)));
5629 if (ajaInfo.leftCol) {
5630 BCPixelSize half = BC_BORDER_RIGHT_HALF(currentBorder.width);
5631 ajaInfo.leftCol->SetLeftBorderWidth(PR_MAX(half, ajaInfo.leftCol->GetLeftBorderWidth()));
5634 // update the top right corner
5635 PRBool hitsSpanOnRight = (rowX > ajaInfo.rowIndex) && (rowX < ajaInfo.rowIndex + ajaInfo.rowSpan);
5636 BCCornerInfo* trCorner = ((0 == rowX) || hitsSpanOnRight)
5637 ? &topCorners[cellEndColIndex + 1] : &bottomCorners[cellEndColIndex + 1];
5638 trCorner->Update(NS_SIDE_BOTTOM, currentBorder);
5639 // if this is not the first time through, consider the segment to the right
5640 if (rowX != info.rowIndex) {
5641 const nsIFrame* rg = (priorAjaInfo.rgBottom) ? priorAjaInfo.rg : nsnull;
5642 currentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, priorAjaInfo.bottomRow, priorAjaInfo.cell,
5643 tableIsLTR, !TABLE_EDGE, NS_SIDE_BOTTOM, ADJACENT);
5644 rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull;
5645 adjacentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell,
5646 tableIsLTR, !TABLE_EDGE, NS_SIDE_TOP, !ADJACENT);
5647 currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, HORIZONTAL);
5648 trCorner->Update(NS_SIDE_RIGHT, currentBorder);
5650 // store the top right corner in the cell map
5651 if (cellEndColIndex < damageArea.XMost() && rowX >= damageArea.y) {
5652 if (0 != rowX) {
5653 tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX, cellEndColIndex,
5654 trCorner->ownerSide, trCorner->subWidth, trCorner->bevel);
5656 // store any corners this cell spans together with the aja cell
5657 for (PRInt32 rX = rowX + 1; rX < rowX + segLength; rX++) {
5658 tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rX,
5659 cellEndColIndex, trCorner->ownerSide, trCorner->subWidth, PR_FALSE);
5662 // update bottom right corner, topCorners, bottomCorners
5663 hitsSpanOnRight = (rowX + segLength < ajaInfo.rowIndex + ajaInfo.rowSpan);
5664 BCCornerInfo& brCorner = (hitsSpanOnRight) ? topCorners[cellEndColIndex + 1]
5665 : bottomCorners[cellEndColIndex + 1];
5666 brCorner.Set(NS_SIDE_TOP, currentBorder);
5667 priorAjaInfo = ajaInfo;
5670 for (PRInt32 colX = info.colIndex + 1; colX <= cellEndColIndex; colX++) {
5671 lastVerBorders[colX].Reset(0,1);
5674 // find the dominant border considering the cell's bottom border, adjacent cells and the table, row group, row
5675 if (numRows == cellEndRowIndex + 1) { // touches bottom edge of table
5676 if (!tableBorderReset[NS_SIDE_BOTTOM]) {
5677 propData->mBottomBorderWidth = 0;
5678 tableBorderReset[NS_SIDE_BOTTOM] = PR_TRUE;
5680 for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) {
5681 nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0();
5682 nsIFrame* cgFrame = colFrame->GetParent(); if (!cgFrame) ABORT0();
5683 currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg, info.bottomRow, info.cell,
5684 tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM, ADJACENT);
5685 // update/store the bottom left & bottom right corners
5686 BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
5687 blCorner.Update(NS_SIDE_RIGHT, currentBorder);
5688 tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,
5689 colX, blCorner.ownerSide, blCorner.subWidth, blCorner.bevel);
5690 BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right
5691 brCorner.Update(NS_SIDE_LEFT, currentBorder);
5692 if (numCols == colX + 1) { // lower right corner of the table
5693 tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,
5694 colX, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel, PR_TRUE);
5696 // update lastBottomBorder and see if a new segment starts
5697 startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
5698 if (!startSeg) {
5699 // make sure that we did not compare apples to oranges i.e. the current border
5700 // should be a continuation of the lastBottomBorder, as it is a bottom border
5701 // add 1 to the cellEndRowIndex
5702 startSeg = (lastBottomBorder.rowIndex != cellEndRowIndex + 1);
5704 // store the border segment in the cell map and update cellBorders
5705 tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,
5706 colX, 1, currentBorder.owner, currentBorder.width, startSeg);
5707 // update the bottom borders of the cell, the bottom row, and the table
5708 if (info.cell) {
5709 info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(NS_SIDE_BOTTOM)));
5711 if (info.bottomRow) {
5712 BCPixelSize half = BC_BORDER_TOP_HALF(currentBorder.width);
5713 info.bottomRow->SetBottomBCBorderWidth(PR_MAX(half, info.bottomRow->GetBottomBCBorderWidth()));
5715 propData->mBottomBorderWidth = LimitBorderWidth(PR_MAX(propData->mBottomBorderWidth, currentBorder.width));
5716 // update lastBottomBorders
5717 lastBottomBorder.rowIndex = cellEndRowIndex + 1;
5718 lastBottomBorder.rowSpan = info.rowSpan;
5719 lastBottomBorders[colX] = lastBottomBorder;
5720 //get col continuous border
5721 currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg, info.bottomRow,
5722 nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM,
5723 ADJACENT);
5724 ((nsTableColFrame*)colFrame)->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM,
5725 currentBorder.width);
5727 //get row group/col group continuous border
5728 if (info.rg) {
5729 currentBorder = CompareBorders(this, nsnull, nsnull, info.rg, info.bottomRow,
5730 nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM,
5731 ADJACENT);
5732 info.rg->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5734 if (info.cg) {
5735 currentBorder = CompareBorders(this, info.cg, nsnull, info.rg, info.bottomRow,
5736 nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM,
5737 ADJACENT);
5738 info.cg->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5741 else {
5742 PRInt32 segLength = 0;
5743 for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX += segLength) {
5744 iter.PeekBottom(info, colX, ajaInfo);
5745 const nsIFrame* rg = (info.rgBottom) ? info.rg : nsnull;
5746 currentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, info.bottomRow, info.cell,
5747 tableIsLTR, !TABLE_EDGE, NS_SIDE_BOTTOM, ADJACENT);
5748 rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull;
5749 adjacentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell,
5750 tableIsLTR, !TABLE_EDGE, NS_SIDE_TOP, !ADJACENT);
5751 currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, HORIZONTAL);
5752 segLength = PR_MAX(1, ajaInfo.colIndex + ajaInfo.colSpan - colX);
5753 segLength = PR_MIN(segLength, info.colIndex + info.colSpan - colX);
5755 // update, store the bottom left corner
5756 BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
5757 PRBool hitsSpanBelow = (colX > ajaInfo.colIndex) && (colX < ajaInfo.colIndex + ajaInfo.colSpan);
5758 PRBool update = PR_TRUE;
5759 if ((colX == info.colIndex) && (colX > damageArea.x)) {
5760 PRInt32 prevRowIndex = lastBottomBorders[colX - 1].rowIndex;
5761 if (prevRowIndex > cellEndRowIndex + 1) { // hits a rowspan on the right
5762 update = PR_FALSE; // the corner was taken care of during the cell on the left
5764 else if (prevRowIndex < cellEndRowIndex + 1) { // spans below the cell to the left
5765 topCorners[colX] = blCorner;
5766 blCorner.Set(NS_SIDE_RIGHT, currentBorder);
5767 update = PR_FALSE;
5770 if (update) {
5771 blCorner.Update(NS_SIDE_RIGHT, currentBorder);
5773 if (cellEndRowIndex < damageArea.YMost() && colX >= damageArea.x) {
5774 if (hitsSpanBelow) {
5775 tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, colX,
5776 blCorner.ownerSide, blCorner.subWidth, blCorner.bevel);
5778 // store any corners this cell spans together with the aja cell
5779 for (PRInt32 cX = colX + 1; cX < colX + segLength; cX++) {
5780 BCCornerInfo& corner = bottomCorners[cX];
5781 corner.Set(NS_SIDE_RIGHT, currentBorder);
5782 tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,
5783 cX, corner.ownerSide, corner.subWidth, PR_FALSE);
5786 // update lastBottomBorders and see if a new segment starts
5787 startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
5788 if (!startSeg) {
5789 // make sure that we did not compare apples to oranges i.e. the current border
5790 // should be a continuation of the lastBottomBorder, as it is a bottom border
5791 // add 1 to the cellEndRowIndex
5792 startSeg = (lastBottomBorder.rowIndex != cellEndRowIndex + 1);
5794 lastBottomBorder.rowIndex = cellEndRowIndex + 1;
5795 lastBottomBorder.rowSpan = info.rowSpan;
5796 for (PRInt32 cX = colX; cX < colX + segLength; cX++) {
5797 lastBottomBorders[cX] = lastBottomBorder;
5800 // store the border segment the cell map and update cellBorders
5801 if (cellEndRowIndex < damageArea.YMost() && colX >= damageArea.x && colX < damageArea.XMost()) {
5802 tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,
5803 colX, segLength, currentBorder.owner, currentBorder.width, startSeg);
5804 // update the borders of the affected cells and rows
5805 if (info.cell) {
5806 info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(NS_SIDE_BOTTOM)));
5808 if (info.bottomRow) {
5809 BCPixelSize half = BC_BORDER_TOP_HALF(currentBorder.width);
5810 info.bottomRow->SetBottomBCBorderWidth(PR_MAX(half, info.bottomRow->GetBottomBCBorderWidth()));
5812 if (ajaInfo.cell) {
5813 ajaInfo.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(currentBorder.width, ajaInfo.cell->GetBorderWidth(NS_SIDE_TOP)));
5815 if (ajaInfo.topRow) {
5816 BCPixelSize half = BC_BORDER_BOTTOM_HALF(currentBorder.width);
5817 ajaInfo.topRow->SetTopBCBorderWidth(PR_MAX(half, ajaInfo.topRow->GetTopBCBorderWidth()));
5820 // update bottom right corner
5821 BCCornerInfo& brCorner = bottomCorners[colX + segLength];
5822 brCorner.Update(NS_SIDE_LEFT, currentBorder);
5824 if (!gotRowBorder && 1 == info.rowSpan && (ajaInfo.topRow || info.rgBottom)) {
5825 //get continuous row/row group border
5826 //we need to check the row group's bottom border if this is
5827 //the last row in the row group, but only a cell with rowspan=1
5828 //will know whether *this* row is at the bottom
5829 const nsIFrame* rg = (info.rgBottom) ? info.rg : nsnull;
5830 currentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, info.bottomRow,
5831 nsnull, tableIsLTR, !TABLE_EDGE, NS_SIDE_BOTTOM,
5832 ADJACENT);
5833 rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull;
5834 adjacentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, ajaInfo.topRow,
5835 nsnull, tableIsLTR, !TABLE_EDGE, NS_SIDE_TOP,
5836 !ADJACENT);
5837 currentBorder = CompareBorders(PR_FALSE, currentBorder, adjacentBorder, HORIZONTAL);
5838 if (ajaInfo.topRow) {
5839 ajaInfo.topRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
5841 if (info.rgBottom && info.rg) {
5842 info.rg->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
5844 gotRowBorder = PR_TRUE;
5848 // see if the cell to the right had a rowspan and its lower left border needs be joined with this one's bottom
5849 if ((numCols != cellEndColIndex + 1) && // there is a cell to the right
5850 (lastBottomBorders[cellEndColIndex + 1].rowSpan > 1)) { // cell to right was a rowspan
5851 BCCornerInfo& corner = bottomCorners[cellEndColIndex + 1];
5852 if ((NS_SIDE_TOP != corner.ownerSide) && (NS_SIDE_BOTTOM != corner.ownerSide)) { // not a vertical owner
5853 BCCellBorder& thisBorder = lastBottomBorder;
5854 BCCellBorder& nextBorder = lastBottomBorders[info.colIndex + 1];
5855 if ((thisBorder.color == nextBorder.color) && (thisBorder.width == nextBorder.width) &&
5856 (thisBorder.style == nextBorder.style)) {
5857 // set the flag on the next border indicating it is not the start of a new segment
5858 if (iter.mCellMap) {
5859 BCData* bcData = tableCellMap->GetBCData(NS_SIDE_BOTTOM, *iter.mCellMap, cellEndRowIndex,
5860 cellEndColIndex + 1);
5861 if (bcData) {
5862 bcData->SetTopStart(PR_FALSE);
5868 } // for (iter.First(info); info.cell; iter.Next(info)) {
5870 // reset the bc flag and damage area
5871 SetNeedToCalcBCBorders(PR_FALSE);
5872 propData->mDamageArea.x = propData->mDamageArea.y = propData->mDamageArea.width = propData->mDamageArea.height = 0;
5873 #ifdef DEBUG_TABLE_CELLMAP
5874 mCellMap->Dump();
5875 #endif
5878 // Iterates over borders (left border, corner, top border) in the cell map within a damage area
5879 // from left to right, top to bottom. All members are in terms of the 1st in flow frames, except
5880 // where suffixed by InFlow.
5881 class BCMapBorderIterator
5883 public:
5884 BCMapBorderIterator(nsTableFrame& aTableFrame,
5885 nsTableRowGroupFrame& aRowGroupFrame,
5886 nsTableRowFrame& aRowFrame,
5887 const nsRect& aDamageArea);
5888 void Reset(nsTableFrame& aTableFrame,
5889 nsTableRowGroupFrame& aRowGroupFrame,
5890 nsTableRowFrame& aRowFrame,
5891 const nsRect& aDamageArea);
5892 void First();
5893 void Next();
5895 nsTableFrame* table;
5896 nsTableCellMap* tableCellMap;
5897 nsCellMap* cellMap;
5899 nsTableFrame::RowGroupArray rowGroups;
5900 nsTableRowGroupFrame* prevRg;
5901 nsTableRowGroupFrame* rg;
5902 PRInt32 rowGroupIndex;
5903 PRInt32 fifRowGroupStart;
5904 PRInt32 rowGroupStart;
5905 PRInt32 rowGroupEnd;
5906 PRInt32 numRows; // number of rows in the table and all continuations
5908 nsTableRowFrame* prevRow;
5909 nsTableRowFrame* row;
5910 PRInt32 numCols;
5911 PRInt32 x;
5912 PRInt32 y;
5914 nsTableCellFrame* prevCell;
5915 nsTableCellFrame* cell;
5916 BCCellData* prevCellData;
5917 BCCellData* cellData;
5918 BCData* bcData;
5920 PRBool IsTopMostTable() { return (y == 0) && !table->GetPrevInFlow(); }
5921 PRBool IsRightMostTable() { return (x >= numCols); }
5922 PRBool IsBottomMostTable() { return (y >= numRows) && !table->GetNextInFlow(); }
5923 PRBool IsLeftMostTable() { return (x == 0); }
5924 PRBool IsTopMost() { return (y == startY); }
5925 PRBool IsRightMost() { return (x >= endX); }
5926 PRBool IsBottomMost() { return (y >= endY); }
5927 PRBool IsLeftMost() { return (x == startX); }
5928 PRBool isNewRow;
5930 PRInt32 startX;
5931 PRInt32 startY;
5932 PRInt32 endX;
5933 PRInt32 endY;
5934 PRBool isRepeatedHeader;
5935 PRBool isRepeatedFooter;
5936 PRBool atEnd;
5938 private:
5940 PRBool SetNewRow(nsTableRowFrame* aRow = nsnull);
5941 PRBool SetNewRowGroup();
5942 void SetNewData(PRInt32 aY, PRInt32 aX);
5946 BCMapBorderIterator::BCMapBorderIterator(nsTableFrame& aTable,
5947 nsTableRowGroupFrame& aRowGroup,
5948 nsTableRowFrame& aRow,
5949 const nsRect& aDamageArea)
5951 Reset(aTable, aRowGroup, aRow, aDamageArea);
5954 void
5955 BCMapBorderIterator::Reset(nsTableFrame& aTable,
5956 nsTableRowGroupFrame& aRowGroup,
5957 nsTableRowFrame& aRow,
5958 const nsRect& aDamageArea)
5960 atEnd = PR_TRUE; // gets reset when First() is called
5962 table = &aTable;
5963 rg = &aRowGroup;
5964 prevRow = nsnull;
5965 row = &aRow;
5967 nsTableFrame* tableFif = (nsTableFrame*)table->GetFirstInFlow(); if (!tableFif) ABORT0();
5968 tableCellMap = tableFif->GetCellMap();
5970 startX = aDamageArea.x;
5971 startY = aDamageArea.y;
5972 endY = aDamageArea.y + aDamageArea.height;
5973 endX = aDamageArea.x + aDamageArea.width;
5975 numRows = tableFif->GetRowCount();
5976 y = 0;
5977 numCols = tableFif->GetColCount();
5978 x = 0;
5979 rowGroupIndex = -1;
5980 prevCell = nsnull;
5981 cell = nsnull;
5982 prevCellData = nsnull;
5983 cellData = nsnull;
5984 bcData = nsnull;
5986 // Get the ordered row groups
5987 table->OrderRowGroups(rowGroups);
5990 void
5991 BCMapBorderIterator::SetNewData(PRInt32 aY,
5992 PRInt32 aX)
5994 if (!tableCellMap || !tableCellMap->mBCInfo) ABORT0();
5996 x = aX;
5997 y = aY;
5998 prevCellData = cellData;
5999 if (IsRightMost() && IsBottomMost()) {
6000 cell = nsnull;
6001 bcData = &tableCellMap->mBCInfo->mLowerRightCorner;
6003 else if (IsRightMost()) {
6004 cellData = nsnull;
6005 bcData = (BCData*)tableCellMap->mBCInfo->mRightBorders.ElementAt(aY);
6007 else if (IsBottomMost()) {
6008 cellData = nsnull;
6009 bcData = (BCData*)tableCellMap->mBCInfo->mBottomBorders.ElementAt(aX);
6011 else {
6012 if (PRUint32(y - fifRowGroupStart) < cellMap->mRows.Length()) {
6013 bcData = nsnull;
6014 cellData =
6015 (BCCellData*)cellMap->mRows[y - fifRowGroupStart].SafeElementAt(x);
6016 if (cellData) {
6017 bcData = &cellData->mData;
6018 if (!cellData->IsOrig()) {
6019 if (cellData->IsRowSpan()) {
6020 aY -= cellData->GetRowSpanOffset();
6022 if (cellData->IsColSpan()) {
6023 aX -= cellData->GetColSpanOffset();
6025 if ((aX >= 0) && (aY >= 0)) {
6026 cellData = (BCCellData*)cellMap->mRows[aY - fifRowGroupStart][aX];
6029 if (cellData->IsOrig()) {
6030 prevCell = cell;
6031 cell = cellData->GetCellFrame();
6038 PRBool
6039 BCMapBorderIterator::SetNewRow(nsTableRowFrame* aRow)
6041 prevRow = row;
6042 row = (aRow) ? aRow : row->GetNextRow();
6044 if (row) {
6045 isNewRow = PR_TRUE;
6046 y = row->GetRowIndex();
6047 x = startX;
6049 else {
6050 atEnd = PR_TRUE;
6052 return !atEnd;
6056 PRBool
6057 BCMapBorderIterator::SetNewRowGroup()
6059 rowGroupIndex++;
6061 isRepeatedHeader = PR_FALSE;
6062 isRepeatedFooter = PR_FALSE;
6064 if (rowGroupIndex < rowGroups.Length()) {
6065 prevRg = rg;
6066 rg = rowGroups[rowGroupIndex];
6067 fifRowGroupStart = ((nsTableRowGroupFrame*)rg->GetFirstInFlow())->GetStartRowIndex();
6068 rowGroupStart = rg->GetStartRowIndex();
6069 rowGroupEnd = rowGroupStart + rg->GetRowCount() - 1;
6071 if (SetNewRow(rg->GetFirstRow())) {
6072 cellMap =
6073 tableCellMap->GetMapFor((nsTableRowGroupFrame*)rg->GetFirstInFlow(),
6074 nsnull);
6075 if (!cellMap) ABORT1(PR_FALSE);
6077 if (rg && table->GetPrevInFlow() && !rg->GetPrevInFlow()) {
6078 // if rg doesn't have a prev in flow, then it may be a repeated header or footer
6079 const nsStyleDisplay* display = rg->GetStyleDisplay();
6080 if (y == startY) {
6081 isRepeatedHeader = (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == display->mDisplay);
6083 else {
6084 isRepeatedFooter = (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay);
6088 else {
6089 atEnd = PR_TRUE;
6091 return !atEnd;
6094 void
6095 BCMapBorderIterator::First()
6097 if (!table || (startX >= numCols) || (startY >= numRows)) ABORT0();
6099 atEnd = PR_FALSE;
6101 PRUint32 numRowGroups = rowGroups.Length();
6102 for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
6103 nsTableRowGroupFrame* rowG = rowGroups[rgX];
6104 PRInt32 start = rowG->GetStartRowIndex();
6105 PRInt32 end = start + rowG->GetRowCount() - 1;
6106 if ((startY >= start) && (startY <= end)) {
6107 rowGroupIndex = rgX - 1; // SetNewRowGroup increments rowGroupIndex
6108 if (SetNewRowGroup()) {
6109 while ((y < startY) && !atEnd) {
6110 SetNewRow();
6112 if (!atEnd) {
6113 SetNewData(startY, startX);
6116 return;
6119 atEnd = PR_TRUE;
6122 void
6123 BCMapBorderIterator::Next()
6125 if (atEnd) ABORT0();
6126 isNewRow = PR_FALSE;
6128 x++;
6129 if (x > endX) {
6130 y++;
6131 if (y == endY) {
6132 x = startX;
6134 else if (y < endY) {
6135 if (y <= rowGroupEnd) {
6136 SetNewRow();
6138 else {
6139 SetNewRowGroup();
6142 else {
6143 atEnd = PR_TRUE;
6146 if (!atEnd) {
6147 SetNewData(y, x);
6151 // XXX if CalcVerCornerOffset and CalcHorCornerOffset remain similar, combine them
6152 static nscoord
6153 CalcVerCornerOffset(PRUint8 aCornerOwnerSide,
6154 nscoord aCornerSubWidth,
6155 nscoord aHorWidth,
6156 PRBool aIsStartOfSeg,
6157 PRBool aIsBevel)
6159 nscoord offset = 0;
6160 // XXX These should be replaced with appropriate side-specific macros (which?).
6161 nscoord smallHalf, largeHalf;
6162 if ((NS_SIDE_TOP == aCornerOwnerSide) || (NS_SIDE_BOTTOM == aCornerOwnerSide)) {
6163 DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
6164 if (aIsBevel) {
6165 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6167 else {
6168 offset = (NS_SIDE_TOP == aCornerOwnerSide) ? smallHalf : -largeHalf;
6171 else {
6172 DivideBCBorderSize(aHorWidth, smallHalf, largeHalf);
6173 if (aIsBevel) {
6174 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6176 else {
6177 offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
6180 return nsPresContext::CSSPixelsToAppUnits(offset);
6183 /** Compute the horizontal offset of a horizontal border segment
6184 * @param aCornerOwnerSide - which side owns the corner
6185 * @param aCornerSubWidth - how wide is the nonwinning side of the corner
6186 * @param aVerWidth - how wide is the vertical edge of the corner
6187 * @param aIsStartOfSeg - does this corner start a new segment
6188 * @param aIsBevel - is this corner beveled
6189 * @param aPixelsToTwips - conversion factor
6190 * @param aTableIsLTR - direction, the computation depends on ltr or rtl
6191 * @return - offset in pixel
6193 static nscoord
6194 CalcHorCornerOffset(PRUint8 aCornerOwnerSide,
6195 nscoord aCornerSubWidth,
6196 nscoord aVerWidth,
6197 PRBool aIsStartOfSeg,
6198 PRBool aIsBevel,
6199 PRBool aTableIsLTR)
6201 nscoord offset = 0;
6202 // XXX These should be replaced with appropriate side-specific macros (which?).
6203 nscoord smallHalf, largeHalf;
6204 if ((NS_SIDE_LEFT == aCornerOwnerSide) || (NS_SIDE_RIGHT == aCornerOwnerSide)) {
6205 if (aTableIsLTR) {
6206 DivideBCBorderSize(aCornerSubWidth, smallHalf, largeHalf);
6208 else {
6209 DivideBCBorderSize(aCornerSubWidth, largeHalf, smallHalf);
6211 if (aIsBevel) {
6212 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6214 else {
6215 offset = (NS_SIDE_LEFT == aCornerOwnerSide) ? smallHalf : -largeHalf;
6218 else {
6219 if (aTableIsLTR) {
6220 DivideBCBorderSize(aVerWidth, smallHalf, largeHalf);
6222 else {
6223 DivideBCBorderSize(aVerWidth, largeHalf, smallHalf);
6225 if (aIsBevel) {
6226 offset = (aIsStartOfSeg) ? -largeHalf : smallHalf;
6228 else {
6229 offset = (aIsStartOfSeg) ? smallHalf : -largeHalf;
6232 return nsPresContext::CSSPixelsToAppUnits(offset);
6235 struct BCVerticalSeg
6237 BCVerticalSeg();
6239 void Start(BCMapBorderIterator& aIter,
6240 BCBorderOwner aBorderOwner,
6241 nscoord aVerSegWidth,
6242 nscoord aPrevHorSegHeight,
6243 nscoord aHorSegHeight,
6244 BCVerticalSeg* aVerInfoArray);
6246 union {
6247 nsTableColFrame* col;
6248 PRInt32 colWidth;
6250 PRInt32 colX;
6251 nsTableCellFrame* ajaCell;
6252 nsTableCellFrame* firstCell; // cell at the start of the segment
6253 nsTableRowGroupFrame* firstRowGroup; // row group at the start of the segment
6254 nsTableRowFrame* firstRow; // row at the start of the segment
6255 nsTableCellFrame* lastCell; // cell at the current end of the segment
6256 PRInt32 segY;
6257 PRInt32 segHeight;
6258 PRInt16 segWidth; // width in pixels
6259 PRUint8 owner;
6260 PRUint8 bevelSide;
6261 PRUint16 bevelOffset;
6264 BCVerticalSeg::BCVerticalSeg()
6266 col = nsnull; firstCell = lastCell = ajaCell = nsnull; colX = segY = segHeight = 0;
6267 segWidth = bevelOffset = 0; bevelSide = 0; owner = eCellOwner;
6270 void
6271 BCVerticalSeg::Start(BCMapBorderIterator& aIter,
6272 BCBorderOwner aBorderOwner,
6273 nscoord aVerSegWidth,
6274 nscoord aPrevHorSegHeight,
6275 nscoord aHorSegHeight,
6276 BCVerticalSeg* aVerInfoArray)
6278 PRUint8 ownerSide = 0;
6279 PRPackedBool bevel = PR_FALSE;
6280 PRInt32 xAdj = aIter.x - aIter.startX;
6282 nscoord cornerSubWidth = (aIter.bcData) ? aIter.bcData->GetCorner(ownerSide, bevel) : 0;
6283 PRBool topBevel = (aVerSegWidth > 0) ? bevel : PR_FALSE;
6284 nscoord maxHorSegHeight = PR_MAX(aPrevHorSegHeight, aHorSegHeight);
6285 nscoord offset = CalcVerCornerOffset(ownerSide, cornerSubWidth, maxHorSegHeight,
6286 PR_TRUE, topBevel);
6288 bevelOffset = (topBevel) ? maxHorSegHeight : 0;
6289 bevelSide = (aHorSegHeight > 0) ? NS_SIDE_RIGHT : NS_SIDE_LEFT;
6290 segY += offset;
6291 segHeight = -offset;
6292 segWidth = aVerSegWidth;
6293 owner = aBorderOwner;
6294 firstCell = aIter.cell;
6295 firstRowGroup = aIter.rg;
6296 firstRow = aIter.row;
6297 if (xAdj > 0) {
6298 ajaCell = aVerInfoArray[xAdj - 1].lastCell;
6302 struct BCHorizontalSeg
6304 BCHorizontalSeg();
6306 void Start(BCMapBorderIterator& aIter,
6307 BCBorderOwner aBorderOwner,
6308 PRUint8 aCornerOwnerSide,
6309 nscoord aSubWidth,
6310 PRBool aBevel,
6311 nscoord aTopVerSegWidth,
6312 nscoord aBottomVerSegWidth,
6313 nscoord aHorSegHeight,
6314 nsTableCellFrame* aLastCell,
6315 PRBool aTableIsLTR);
6317 nscoord x;
6318 nscoord y;
6319 nscoord width;
6320 nscoord height;
6321 PRBool leftBevel;
6322 nscoord leftBevelOffset;
6323 PRUint8 leftBevelSide;
6324 PRUint8 owner;
6325 nsTableCellFrame* firstCell; // cell at the start of the segment
6326 nsTableCellFrame* ajaCell;
6329 BCHorizontalSeg::BCHorizontalSeg()
6331 x = y = width = height = leftBevel = leftBevelOffset = leftBevelSide = 0;
6332 firstCell = ajaCell = nsnull;
6335 /** Initialize a horizontal border segment for painting
6336 * @param aIter - iterator storing the current and adjacent frames
6337 * @param aBorderOwner - which frame owns the border
6338 * @param aCornerOwnerSide - which side owns the starting corner
6339 * @param aSubWidth - how wide is the nonowning width of the corner
6340 * @param aBevel - is the corner beveled
6341 * @param aTopVerSegWidth - vertical segment width going down
6342 * @param aBottomVerSegWidth - vertical segment width coming from up
6343 * @param aHorSegHeight - the height of the segment
6344 * @param aLastCell - cell frame above this segment
6345 * @param aPixelsToTwips - conversion factor
6346 * @param aTableIsLTR - direction, the computation depends on ltr or rtl
6348 void
6349 BCHorizontalSeg::Start(BCMapBorderIterator& aIter,
6350 BCBorderOwner aBorderOwner,
6351 PRUint8 aCornerOwnerSide,
6352 nscoord aSubWidth,
6353 PRBool aBevel,
6354 nscoord aTopVerSegWidth,
6355 nscoord aBottomVerSegWidth,
6356 nscoord aHorSegHeight,
6357 nsTableCellFrame* aLastCell,
6358 PRBool aTableIsLTR)
6360 owner = aBorderOwner;
6361 leftBevel = (aHorSegHeight > 0) ? aBevel : PR_FALSE;
6362 nscoord maxVerSegWidth = PR_MAX(aTopVerSegWidth, aBottomVerSegWidth);
6363 nscoord offset = CalcHorCornerOffset(aCornerOwnerSide, aSubWidth, maxVerSegWidth,
6364 PR_TRUE, leftBevel, aTableIsLTR);
6365 leftBevelOffset = (leftBevel && (aHorSegHeight > 0)) ? maxVerSegWidth : 0;
6366 leftBevelSide = (aBottomVerSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
6367 if (aTableIsLTR) {
6368 x += offset;
6370 else {
6371 x -= offset;
6373 width = -offset;
6374 height = aHorSegHeight;
6375 firstCell = aIter.cell;
6376 ajaCell = (aIter.IsTopMost()) ? nsnull : aLastCell;
6379 void
6380 nsTableFrame::PaintBCBorders(nsIRenderingContext& aRenderingContext,
6381 const nsRect& aDirtyRect)
6383 nsMargin childAreaOffset = GetChildAreaOffset(nsnull);
6384 nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT0();
6386 PRInt32 startRowY = (GetPrevInFlow()) ? 0 : childAreaOffset.top; // y position of first row in damage area
6388 const nsStyleBackground* bgColor = nsCSSRendering::FindNonTransparentBackground(mStyleContext);
6389 // determine the damage area in terms of rows and columns and finalize startColX and startRowY
6390 PRUint32 startRowIndex, endRowIndex, startColIndex, endColIndex;
6391 startRowIndex = endRowIndex = startColIndex = endColIndex = 0;
6393 RowGroupArray rowGroups;
6394 OrderRowGroups(rowGroups);
6395 PRBool done = PR_FALSE;
6396 PRBool haveIntersect = PR_FALSE;
6397 nsTableRowGroupFrame* inFlowRG = nsnull;
6398 nsTableRowFrame* inFlowRow = nsnull;
6399 // find startRowIndex, endRowIndex, startRowY
6400 PRInt32 rowY = startRowY;
6401 for (PRUint32 rgX = 0; rgX < rowGroups.Length() && !done; rgX++) {
6402 nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
6403 for (nsTableRowFrame* rowFrame = rgFrame->GetFirstRow(); rowFrame;
6404 rowFrame = rowFrame->GetNextRow()) {
6405 // conservatively estimate the half border widths outside the row
6406 nscoord topBorderHalf = (GetPrevInFlow()) ? 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetTopBCBorderWidth() + 1);
6407 nscoord bottomBorderHalf = (GetNextInFlow()) ? 0 : nsPresContext::CSSPixelsToAppUnits(rowFrame->GetBottomBCBorderWidth() + 1);
6408 // get the row rect relative to the table rather than the row group
6409 nsSize rowSize = rowFrame->GetSize();
6410 if (haveIntersect) {
6411 if (aDirtyRect.YMost() >= (rowY - topBorderHalf)) {
6412 nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0();
6413 endRowIndex = fifRow->GetRowIndex();
6415 else done = PR_TRUE;
6417 else {
6418 if ((rowY + rowSize.height + bottomBorderHalf) >= aDirtyRect.y) {
6419 inFlowRG = rgFrame;
6420 inFlowRow = rowFrame;
6421 nsTableRowFrame* fifRow = (nsTableRowFrame*)rowFrame->GetFirstInFlow(); if (!fifRow) ABORT0();
6422 startRowIndex = endRowIndex = fifRow->GetRowIndex();
6423 haveIntersect = PR_TRUE;
6425 else {
6426 startRowY += rowSize.height;
6429 rowY += rowSize.height;
6432 // outer table borders overflow the table, so the table might be
6433 // target to other areas as the NS_FRAME_OUTSIDE_CHILDREN is set
6434 // on the table
6435 if (!haveIntersect)
6436 return;
6437 if (!inFlowRG || !inFlowRow) ABORT0();
6439 PRInt32 startColX;
6440 // find startColIndex, endColIndex, startColX
6441 haveIntersect = PR_FALSE;
6442 PRUint32 numCols = GetColCount();
6443 if (0 == numCols) return;
6445 PRInt32 leftCol, rightCol, colInc; // columns are in the range [leftCol, rightCol)
6446 PRBool tableIsLTR = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
6447 if (tableIsLTR) {
6448 startColX = childAreaOffset.left; // x position of first col in damage area
6449 leftCol = 0;
6450 rightCol = numCols;
6451 colInc = 1;
6452 } else {
6453 startColX = mRect.width - childAreaOffset.right; // x position of first col in damage area
6454 leftCol = numCols-1;
6455 rightCol = -1;
6456 colInc = -1;
6459 nscoord x = 0;
6460 PRInt32 colX;
6461 for (colX = leftCol; colX != rightCol; colX += colInc) {
6462 nsTableColFrame* colFrame = firstInFlow->GetColFrame(colX);
6463 if (!colFrame) ABORT0();
6464 // conservatively estimate the half border widths outside the col
6465 nscoord leftBorderHalf = nsPresContext::CSSPixelsToAppUnits(colFrame->GetLeftBorderWidth() + 1);
6466 nscoord rightBorderHalf = nsPresContext::CSSPixelsToAppUnits(colFrame->GetRightBorderWidth() + 1);
6467 // get the col rect relative to the table rather than the col group
6468 nsSize size = colFrame->GetSize();
6469 if (haveIntersect) {
6470 if (aDirtyRect.XMost() >= (x - leftBorderHalf)) {
6471 endColIndex = colX;
6473 else break;
6475 else {
6476 if ((x + size.width + rightBorderHalf) >= aDirtyRect.x) {
6477 startColIndex = endColIndex = colX;
6478 haveIntersect = PR_TRUE;
6480 else {
6481 startColX += colInc * size.width;
6484 x += size.width;
6487 if (!tableIsLTR) {
6488 PRUint32 temp;
6489 startColX = mRect.width - childAreaOffset.right;
6490 temp = startColIndex; startColIndex = endColIndex; endColIndex = temp;
6491 for (PRUint32 column = 0; column < startColIndex; column++) {
6492 nsTableColFrame* colFrame = firstInFlow->GetColFrame(column);
6493 if (!colFrame) ABORT0();
6494 nsSize size = colFrame->GetSize();
6495 startColX += colInc * size.width;
6498 if (!haveIntersect)
6499 return;
6500 // iterate the cell map and build up border segments
6501 nsRect damageArea(startColIndex, startRowIndex,
6502 1 + PR_ABS(PRInt32(endColIndex - startColIndex)),
6503 1 + endRowIndex - startRowIndex);
6504 BCVerticalSeg* verInfo = new BCVerticalSeg[damageArea.width + 1]; if (!verInfo) ABORT0();
6506 BCBorderOwner borderOwner, ignoreBorderOwner;
6507 PRUint8 ownerSide;
6508 nscoord cornerSubWidth, smallHalf, largeHalf;
6509 nsRect rowRect(0,0,0,0);
6510 PRBool isSegStart, ignoreSegStart;
6511 nscoord prevHorSegHeight = 0;
6512 PRPackedBool bevel;
6513 PRInt32 repeatedHeaderY = -99;
6514 PRBool afterRepeatedHeader = PR_FALSE;
6515 PRBool startRepeatedFooter = PR_FALSE;
6517 // First, paint all of the vertical borders from top to bottom and left to right as they become complete
6518 // They are painted first, since they are less efficient to paint than horizontal segments. They were
6519 // stored with as few segments as possible (since horizontal borders are painted last and possibly over them).
6520 BCMapBorderIterator iter(*this, *inFlowRG, *inFlowRow, damageArea);
6521 for (iter.First(); !iter.atEnd; iter.Next()) {
6522 nscoord verSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(borderOwner, isSegStart) : 0;
6523 nscoord horSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(ignoreBorderOwner, ignoreSegStart) : 0;
6525 PRInt32 xAdj = iter.x - iter.startX;
6526 if (iter.isNewRow) {
6527 prevHorSegHeight = 0;
6528 rowRect = iter.row->GetRect();
6529 if (iter.isRepeatedHeader) {
6530 repeatedHeaderY = iter.y;
6532 afterRepeatedHeader = !iter.isRepeatedHeader && (iter.y == (repeatedHeaderY + 1));
6533 startRepeatedFooter = iter.isRepeatedFooter && (iter.y == iter.rowGroupStart) && (iter.y != iter.startY);
6535 BCVerticalSeg& info = verInfo[xAdj];
6536 if (!info.col) { // on the first damaged row and the first segment in the col
6537 info.col = iter.IsRightMostTable() ? verInfo[xAdj - 1].col : firstInFlow->GetColFrame(iter.x);
6538 if (!info.col) ABORT0();
6539 if (0 == xAdj) {
6540 info.colX = startColX;
6542 // set colX for the next column
6543 if (!iter.IsRightMost()) {
6544 verInfo[xAdj + 1].colX = info.colX + colInc * info.col->GetSize().width;
6546 info.segY = startRowY;
6547 info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, verInfo);
6548 info.lastCell = iter.cell;
6551 if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost() || afterRepeatedHeader || startRepeatedFooter)) {
6552 // paint the previous seg or the current one if iter.IsBottomMost()
6553 if (info.segHeight > 0) {
6554 if (iter.bcData) {
6555 cornerSubWidth = iter.bcData->GetCorner(ownerSide, bevel);
6556 } else {
6557 cornerSubWidth = 0;
6558 ownerSide = 0; // ???
6559 bevel = PR_FALSE; // ???
6561 PRBool endBevel = (info.segWidth > 0) ? bevel : PR_FALSE;
6562 nscoord bottomHorSegHeight = PR_MAX(prevHorSegHeight, horSegHeight);
6563 nscoord endOffset = CalcVerCornerOffset(ownerSide, cornerSubWidth, bottomHorSegHeight,
6564 PR_FALSE, endBevel);
6565 info.segHeight += endOffset;
6566 if (info.segWidth > 0) {
6567 // get the border style, color and paint the segment
6568 PRUint8 side = (iter.IsRightMost()) ? NS_SIDE_RIGHT : NS_SIDE_LEFT;
6569 nsTableRowFrame* row = info.firstRow;
6570 nsTableRowGroupFrame* rowGroup = info.firstRowGroup;
6571 nsTableColFrame* col = info.col; if (!col) ABORT0();
6572 nsTableCellFrame* cell = info.firstCell;
6573 PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID;
6574 nscolor color = 0xFFFFFFFF;
6575 PRBool ignoreIfRules = (iter.IsRightMostTable() || iter.IsLeftMostTable());
6577 switch (info.owner) {
6578 case eTableOwner:
6579 ::GetPaintStyleInfo(this, side, style, color, tableIsLTR, PR_FALSE);
6580 break;
6581 case eAjaColGroupOwner:
6582 side = NS_SIDE_RIGHT;
6583 if (!iter.IsRightMostTable() && (xAdj > 0)) {
6584 col = verInfo[xAdj - 1].col;
6585 } // and fall through
6586 case eColGroupOwner:
6587 if (col) {
6588 nsIFrame* cg = col->GetParent();
6589 if (cg) {
6590 ::GetPaintStyleInfo(cg, side, style, color, tableIsLTR, ignoreIfRules);
6593 break;
6594 case eAjaColOwner:
6595 side = NS_SIDE_RIGHT;
6596 if (!iter.IsRightMostTable() && (xAdj > 0)) {
6597 col = verInfo[xAdj - 1].col;
6598 } // and fall through
6599 case eColOwner:
6600 if (col) {
6601 ::GetPaintStyleInfo(col, side, style, color, tableIsLTR, ignoreIfRules);
6603 break;
6604 case eAjaRowGroupOwner:
6605 NS_ASSERTION(PR_FALSE, "program error"); // and fall through
6606 case eRowGroupOwner:
6607 NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error");
6608 if (rowGroup) {
6609 ::GetPaintStyleInfo(rowGroup, side, style, color, tableIsLTR, ignoreIfRules);
6611 break;
6612 case eAjaRowOwner:
6613 NS_ASSERTION(PR_FALSE, "program error"); // and fall through
6614 case eRowOwner:
6615 NS_ASSERTION(iter.IsLeftMostTable() || iter.IsRightMostTable(), "program error");
6616 if (row) {
6617 ::GetPaintStyleInfo(row, side, style, color, tableIsLTR, ignoreIfRules);
6619 break;
6620 case eAjaCellOwner:
6621 side = NS_SIDE_RIGHT;
6622 cell = info.ajaCell; // and fall through
6623 case eCellOwner:
6624 if (cell) {
6625 ::GetPaintStyleInfo(cell, side, style, color, tableIsLTR, PR_FALSE);
6627 break;
6629 DivideBCBorderSize(info.segWidth, smallHalf, largeHalf);
6630 nsRect segRect(info.colX - nsPresContext::CSSPixelsToAppUnits(largeHalf), info.segY,
6631 nsPresContext::CSSPixelsToAppUnits(info.segWidth), info.segHeight);
6632 nscoord bottomBevelOffset = (endBevel) ? nsPresContext::CSSPixelsToAppUnits(bottomHorSegHeight) : 0;
6633 PRUint8 bottomBevelSide = ((horSegHeight > 0) ^ !tableIsLTR) ? NS_SIDE_RIGHT : NS_SIDE_LEFT;
6634 PRUint8 topBevelSide = ((info.bevelSide == NS_SIDE_RIGHT) ^ !tableIsLTR)? NS_SIDE_RIGHT : NS_SIDE_LEFT;
6635 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, nsPresContext::AppUnitsPerCSSPixel(),
6636 topBevelSide, nsPresContext::CSSPixelsToAppUnits(info.bevelOffset),
6637 bottomBevelSide, bottomBevelOffset);
6638 } // if (info.segWidth > 0)
6639 info.segY = info.segY + info.segHeight - endOffset;
6640 } // if (info.segHeight > 0)
6641 info.Start(iter, borderOwner, verSegWidth, prevHorSegHeight, horSegHeight, verInfo);
6642 } // if (!iter.IsTopMost() && (isSegStart || iter.IsBottomMost()))
6644 info.lastCell = iter.cell;
6645 info.segHeight += rowRect.height;
6646 prevHorSegHeight = horSegHeight;
6647 } // for (iter.First(); !iter.atEnd; iter.Next())
6649 // Next, paint all of the horizontal border segments from top to bottom reuse the verInfo
6650 // array to keep tract of col widths and vertical segments for corner calculations
6651 memset(verInfo, 0, damageArea.width * sizeof(BCVerticalSeg)); // XXX reinitialize properly
6652 for (PRInt32 xIndex = 0; xIndex < damageArea.width; xIndex++) {
6653 verInfo[xIndex].colWidth = -1;
6655 PRInt32 nextY = startRowY;
6656 BCHorizontalSeg horSeg;
6658 iter.Reset(*this, *inFlowRG, *inFlowRow, damageArea);
6659 for (iter.First(); !iter.atEnd; iter.Next()) {
6660 nscoord leftSegWidth = (iter.bcData) ? iter.bcData->GetLeftEdge(ignoreBorderOwner, ignoreSegStart) : 0;
6661 nscoord topSegHeight = (iter.bcData) ? iter.bcData->GetTopEdge(borderOwner, isSegStart) : 0;
6663 PRInt32 xAdj = iter.x - iter.startX;
6664 // store the current col width if it hasn't been already
6665 if (verInfo[xAdj].colWidth < 0) {
6666 if (iter.IsRightMostTable()) {
6667 verInfo[xAdj].colWidth = verInfo[xAdj - 1].colWidth;
6669 else {
6670 nsTableColFrame* col = firstInFlow->GetColFrame(iter.x); if (!col) ABORT0();
6671 verInfo[xAdj].colWidth = col->GetSize().width;
6674 cornerSubWidth = (iter.bcData) ? iter.bcData->GetCorner(ownerSide, bevel) : 0;
6675 nscoord verWidth = PR_MAX(verInfo[xAdj].segWidth, leftSegWidth);
6676 if (iter.isNewRow || (iter.IsLeftMost() && iter.IsBottomMost())) {
6677 horSeg.y = nextY;
6678 nextY = nextY + iter.row->GetSize().height;
6679 horSeg.x = startColX;
6680 horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth,
6681 leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, tableIsLTR);
6683 PRBool verOwnsCorner = (NS_SIDE_TOP == ownerSide) || (NS_SIDE_BOTTOM == ownerSide);
6684 if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner)) {
6685 // paint the previous seg or the current one if iter.IsRightMost()
6686 if (horSeg.width > 0) {
6687 PRBool endBevel = (horSeg.height > 0) ? bevel : 0;
6688 nscoord endOffset = CalcHorCornerOffset(ownerSide, cornerSubWidth, verWidth, PR_FALSE, endBevel, tableIsLTR);
6689 horSeg.width += endOffset;
6690 if (horSeg.height > 0) {
6691 // get the border style, color and paint the segment
6692 PRUint8 side = (iter.IsBottomMost()) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
6693 nsIFrame* rg = iter.rg; if (!rg) ABORT0();
6694 nsIFrame* row = iter.row; if (!row) ABORT0();
6695 nsIFrame* cell = horSeg.firstCell; if (!cell) ABORT0();
6696 nsIFrame* col;
6698 PRUint8 style = NS_STYLE_BORDER_STYLE_SOLID;
6699 nscolor color = 0xFFFFFFFF;
6700 PRBool ignoreIfRules = (iter.IsTopMostTable() || iter.IsBottomMostTable());
6702 switch (horSeg.owner) {
6703 case eTableOwner:
6704 ::GetPaintStyleInfo(this, side, style, color, tableIsLTR, PR_FALSE);
6705 break;
6706 case eAjaColGroupOwner:
6707 NS_ASSERTION(PR_FALSE, "program error"); // and fall through
6708 case eColGroupOwner: {
6709 NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error");
6710 col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0();
6711 nsIFrame* cg = col->GetParent(); if (!cg) ABORT0();
6712 ::GetPaintStyleInfo(cg, side, style, color, tableIsLTR, ignoreIfRules);
6713 break;
6715 case eAjaColOwner:
6716 NS_ASSERTION(PR_FALSE, "program error"); // and fall through
6717 case eColOwner:
6718 NS_ASSERTION(iter.IsTopMostTable() || iter.IsBottomMostTable(), "program error");
6719 col = firstInFlow->GetColFrame(iter.x - 1); if (!col) ABORT0();
6720 ::GetPaintStyleInfo(col, side, style, color, tableIsLTR, ignoreIfRules);
6721 break;
6722 case eAjaRowGroupOwner:
6723 side = NS_SIDE_BOTTOM;
6724 rg = (iter.IsBottomMostTable()) ? iter.rg : iter.prevRg; // and fall through
6725 case eRowGroupOwner:
6726 if (rg) {
6727 ::GetPaintStyleInfo(rg, side, style, color, tableIsLTR, ignoreIfRules);
6729 break;
6730 case eAjaRowOwner:
6731 side = NS_SIDE_BOTTOM;
6732 row = (iter.IsBottomMostTable()) ? iter.row : iter.prevRow; // and fall through
6733 case eRowOwner:
6734 if (row) {
6735 ::GetPaintStyleInfo(row, side, style, color, tableIsLTR, iter.IsBottomMostTable());
6737 break;
6738 case eAjaCellOwner:
6739 side = NS_SIDE_BOTTOM;
6740 // if this is null due to the damage area origin-y > 0, then the border won't show up anyway
6741 cell = horSeg.ajaCell;
6742 // and fall through
6743 case eCellOwner:
6744 if (cell) {
6745 ::GetPaintStyleInfo(cell, side, style, color, tableIsLTR, PR_FALSE);
6747 break;
6750 DivideBCBorderSize(horSeg.height, smallHalf, largeHalf);
6751 nsRect segRect(horSeg.x, horSeg.y - nsPresContext::CSSPixelsToAppUnits(largeHalf), horSeg.width,
6752 nsPresContext::CSSPixelsToAppUnits(horSeg.height));
6753 if (!tableIsLTR)
6754 segRect.x -= segRect.width;
6756 nscoord rightBevelOffset = (endBevel) ? nsPresContext::CSSPixelsToAppUnits(verWidth) : 0;
6757 PRUint8 rightBevelSide = (leftSegWidth > 0) ? NS_SIDE_BOTTOM : NS_SIDE_TOP;
6758 if (tableIsLTR) {
6759 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, nsPresContext::AppUnitsPerCSSPixel(), horSeg.leftBevelSide,
6760 nsPresContext::CSSPixelsToAppUnits(horSeg.leftBevelOffset),
6761 rightBevelSide, rightBevelOffset);
6763 else {
6764 nsCSSRendering::DrawTableBorderSegment(aRenderingContext, style, color, bgColor, segRect, nsPresContext::AppUnitsPerCSSPixel(), rightBevelSide, rightBevelOffset,
6765 horSeg.leftBevelSide, nsPresContext::CSSPixelsToAppUnits(horSeg.leftBevelOffset));
6768 } // if (horSeg.height > 0)
6769 horSeg.x += colInc * (horSeg.width - endOffset);
6770 } // if (horSeg.width > 0)
6771 horSeg.Start(iter, borderOwner, ownerSide, cornerSubWidth, bevel, verInfo[xAdj].segWidth,
6772 leftSegWidth, topSegHeight, verInfo[xAdj].lastCell, tableIsLTR);
6773 } // if (!iter.IsLeftMost() && (isSegStart || iter.IsRightMost() || verOwnsCorner))
6774 horSeg.width += verInfo[xAdj].colWidth;
6775 verInfo[xAdj].segWidth = leftSegWidth;
6776 verInfo[xAdj].lastCell = iter.cell;
6778 delete [] verInfo;
6781 #ifdef DEBUG
6783 static PRBool
6784 GetFrameTypeName(nsIAtom* aFrameType,
6785 char* aName)
6787 PRBool isTable = PR_FALSE;
6788 if (nsGkAtoms::tableOuterFrame == aFrameType)
6789 strcpy(aName, "Tbl");
6790 else if (nsGkAtoms::tableFrame == aFrameType) {
6791 strcpy(aName, "Tbl");
6792 isTable = PR_TRUE;
6794 else if (nsGkAtoms::tableRowGroupFrame == aFrameType)
6795 strcpy(aName, "RowG");
6796 else if (nsGkAtoms::tableRowFrame == aFrameType)
6797 strcpy(aName, "Row");
6798 else if (IS_TABLE_CELL(aFrameType))
6799 strcpy(aName, "Cell");
6800 else if (nsGkAtoms::blockFrame == aFrameType)
6801 strcpy(aName, "Block");
6802 else
6803 NS_ASSERTION(PR_FALSE, "invalid call to GetFrameTypeName");
6805 return isTable;
6807 #endif
6809 PRBool nsTableFrame::RowHasSpanningCells(PRInt32 aRowIndex, PRInt32 aNumEffCols)
6811 PRBool result = PR_FALSE;
6812 nsTableCellMap* cellMap = GetCellMap();
6813 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
6814 if (cellMap) {
6815 result = cellMap->RowHasSpanningCells(aRowIndex, aNumEffCols);
6817 return result;
6820 PRBool nsTableFrame::RowIsSpannedInto(PRInt32 aRowIndex, PRInt32 aNumEffCols)
6822 PRBool result = PR_FALSE;
6823 nsTableCellMap* cellMap = GetCellMap();
6824 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
6825 if (cellMap) {
6826 result = cellMap->RowIsSpannedInto(aRowIndex, aNumEffCols);
6828 return result;
6831 PRBool nsTableFrame::ColHasSpanningCells(PRInt32 aColIndex)
6833 PRBool result = PR_FALSE;
6834 nsTableCellMap * cellMap = GetCellMap();
6835 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
6836 if (cellMap) {
6837 result = cellMap->ColHasSpanningCells(aColIndex);
6839 return result;
6842 PRBool nsTableFrame::ColIsSpannedInto(PRInt32 aColIndex)
6844 PRBool result = PR_FALSE;
6845 nsTableCellMap * cellMap = GetCellMap();
6846 NS_PRECONDITION (cellMap, "bad call, cellMap not yet allocated.");
6847 if (cellMap) {
6848 result = cellMap->ColIsSpannedInto(aColIndex);
6850 return result;
6853 // Destructor function for nscoord properties
6854 static void
6855 DestroyCoordFunc(void* aFrame,
6856 nsIAtom* aPropertyName,
6857 void* aPropertyValue,
6858 void* aDtorData)
6860 delete static_cast<nscoord*>(aPropertyValue);
6863 // Destructor function point properties
6864 static void
6865 DestroyPointFunc(void* aFrame,
6866 nsIAtom* aPropertyName,
6867 void* aPropertyValue,
6868 void* aDtorData)
6870 delete static_cast<nsPoint*>(aPropertyValue);
6873 // Destructor function for nscoord properties
6874 static void
6875 DestroyBCPropertyDataFunc(void* aFrame,
6876 nsIAtom* aPropertyName,
6877 void* aPropertyValue,
6878 void* aDtorData)
6880 delete static_cast<BCPropertyData*>(aPropertyValue);
6883 void*
6884 nsTableFrame::GetProperty(nsIFrame* aFrame,
6885 nsIAtom* aPropertyName,
6886 PRBool aCreateIfNecessary)
6888 nsPropertyTable *propTable = aFrame->PresContext()->PropertyTable();
6889 void *value = propTable->GetProperty(aFrame, aPropertyName);
6890 if (value) {
6891 return (nsPoint*)value; // the property already exists
6893 if (aCreateIfNecessary) {
6894 // The property isn't set yet, so allocate a new value, set the property,
6895 // and return the newly allocated value
6896 NSPropertyDtorFunc dtorFunc = nsnull;
6897 if (aPropertyName == nsGkAtoms::collapseOffsetProperty) {
6898 value = new nsPoint(0, 0);
6899 dtorFunc = DestroyPointFunc;
6901 else if (aPropertyName == nsGkAtoms::rowUnpaginatedHeightProperty) {
6902 value = new nscoord;
6903 dtorFunc = DestroyCoordFunc;
6905 else if (aPropertyName == nsGkAtoms::tableBCProperty) {
6906 value = new BCPropertyData;
6907 dtorFunc = DestroyBCPropertyDataFunc;
6909 if (value) {
6910 propTable->SetProperty(aFrame, aPropertyName, value, dtorFunc, nsnull);
6912 return value;
6914 return nsnull;
6917 /* static */
6918 void
6919 nsTableFrame::InvalidateFrame(nsIFrame* aFrame,
6920 const nsRect& aOrigRect,
6921 const nsRect& aOrigOverflowRect,
6922 PRBool aIsFirstReflow)
6924 nsIFrame* parent = aFrame->GetParent();
6925 NS_ASSERTION(parent, "What happened here?");
6927 if (parent->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
6928 // Don't bother; we'll invalidate the parent's overflow rect when
6929 // we finish reflowing it.
6930 return;
6933 // The part that looks at both the rect and the overflow rect is a
6934 // bit of a hack. See nsBlockFrame::ReflowLine for an eloquent
6935 // description of its hackishness.
6936 nsRect overflowRect = aFrame->GetOverflowRect();
6937 if (aIsFirstReflow ||
6938 aOrigRect.TopLeft() != aFrame->GetPosition() ||
6939 aOrigOverflowRect.TopLeft() != overflowRect.TopLeft()) {
6940 // Invalidate the old and new overflow rects. Note that if the
6941 // frame moved, we can't just use aOrigOverflowRect, since it's in
6942 // coordinates relative to the old position. So invalidate via
6943 // aFrame's parent, and reposition that overflow rect to the right
6944 // place.
6945 // XXXbz this doesn't handle outlines, does it?
6946 aFrame->Invalidate(overflowRect);
6947 parent->Invalidate(aOrigOverflowRect + aOrigRect.TopLeft());
6948 } else {
6949 nsRect rect = aFrame->GetRect();
6950 aFrame->CheckInvalidateSizeChange(aOrigRect, aOrigOverflowRect,
6951 rect.Size());
6952 aFrame->InvalidateRectDifference(aOrigOverflowRect, overflowRect);
6953 parent->InvalidateRectDifference(aOrigRect, rect);
6957 #ifdef DEBUG
6958 #define MAX_SIZE 128
6959 #define MIN_INDENT 30
6961 static
6962 void DumpTableFramesRecur(nsIFrame* aFrame,
6963 PRUint32 aIndent)
6965 char indent[MAX_SIZE + 1];
6966 aIndent = PR_MIN(aIndent, MAX_SIZE - MIN_INDENT);
6967 memset (indent, ' ', aIndent + MIN_INDENT);
6968 indent[aIndent + MIN_INDENT] = 0;
6970 char fName[MAX_SIZE];
6971 nsIAtom* fType = aFrame->GetType();
6972 GetFrameTypeName(fType, fName);
6974 printf("%s%s %p", indent, fName, aFrame);
6975 nsIFrame* flowFrame = aFrame->GetPrevInFlow();
6976 if (flowFrame) {
6977 printf(" pif=%p", flowFrame);
6979 flowFrame = aFrame->GetNextInFlow();
6980 if (flowFrame) {
6981 printf(" nif=%p", flowFrame);
6983 printf("\n");
6985 if (nsGkAtoms::tableFrame == fType ||
6986 nsGkAtoms::tableRowGroupFrame == fType ||
6987 nsGkAtoms::tableRowFrame == fType ||
6988 IS_TABLE_CELL(fType)) {
6989 nsIFrame* child = aFrame->GetFirstChild(nsnull);
6990 while(child) {
6991 DumpTableFramesRecur(child, aIndent+1);
6992 child = child->GetNextSibling();
6997 void
6998 nsTableFrame::DumpTableFrames(nsIFrame* aFrame)
7000 nsTableFrame* tableFrame = nsnull;
7002 if (nsGkAtoms::tableFrame == aFrame->GetType()) {
7003 tableFrame = static_cast<nsTableFrame*>(aFrame);
7005 else {
7006 tableFrame = nsTableFrame::GetTableFrame(aFrame);
7008 tableFrame = static_cast<nsTableFrame*>(tableFrame->GetFirstInFlow());
7009 while (tableFrame) {
7010 DumpTableFramesRecur(tableFrame, 0);
7011 tableFrame = static_cast<nsTableFrame*>(tableFrame->GetNextInFlow());
7014 #endif