Back to index

lightning-sunbird  0.9+nobinonly
nsTableFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *   Mats Palmgren <mats.palmgren@bredband.net>
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 #include "nsCOMPtr.h"
00040 #include "nsVoidArray.h"
00041 #include "nsTableFrame.h"
00042 #include "nsIRenderingContext.h"
00043 #include "nsStyleContext.h"
00044 #include "nsStyleConsts.h"
00045 #include "nsIContent.h"
00046 #include "nsCellMap.h"
00047 #include "nsTableCellFrame.h"
00048 #include "nsHTMLParts.h"
00049 #include "nsTableColFrame.h"
00050 #include "nsTableColGroupFrame.h"
00051 #include "nsTableRowFrame.h"
00052 #include "nsTableRowGroupFrame.h"
00053 #include "nsTableOuterFrame.h"
00054 #include "nsTablePainter.h"
00055 
00056 #include "BasicTableLayoutStrategy.h"
00057 #include "FixedTableLayoutStrategy.h"
00058 
00059 #include "nsPresContext.h"
00060 #include "nsCSSRendering.h"
00061 #include "nsStyleConsts.h"
00062 #include "nsVoidArray.h"
00063 #include "nsIView.h"
00064 #include "nsHTMLAtoms.h"
00065 #include "nsCSSAnonBoxes.h"
00066 #include "nsHTMLReflowCommand.h"
00067 #include "nsLayoutAtoms.h"
00068 #include "nsIDeviceContext.h"
00069 #include "nsIPresShell.h"
00070 #include "nsIDOMElement.h"
00071 #include "nsIDOMHTMLElement.h"
00072 #include "nsIDOMHTMLBodyElement.h"
00073 #include "nsIScrollableFrame.h"
00074 #include "nsHTMLReflowCommand.h"
00075 #include "nsFrameManager.h"
00076 #include "nsCSSRendering.h"
00077 #include "nsLayoutErrors.h"
00078 #include "nsAutoPtr.h"
00079 #include "nsCSSFrameConstructor.h"
00080 #include "nsStyleSet.h"
00081 
00082 
00083 /********************************************************************************
00084  ** nsTableReflowState                                                         **
00085  ********************************************************************************/
00086 
00087 struct nsTableReflowState {
00088 
00089   // the real reflow state
00090   const nsHTMLReflowState& reflowState;
00091 
00092   nsReflowReason reason;
00093 
00094   // The table's available size 
00095   nsSize availSize;
00096 
00097   // Stationary x-offset
00098   nscoord x;
00099 
00100   // Running y-offset
00101   nscoord y;
00102 
00103   // Pointer to the footer in the table
00104   nsIFrame* footerFrame;
00105 
00106   // The first body section row group frame, i.e. not a header or footer
00107   nsIFrame* firstBodySection;
00108 
00109   nsTableReflowState(nsPresContext&          aPresContext,
00110                      const nsHTMLReflowState& aReflowState,
00111                      nsTableFrame&            aTableFrame,
00112                      nsReflowReason           aReason,
00113                      nscoord                  aAvailWidth,
00114                      nscoord                  aAvailHeight)
00115     : reflowState(aReflowState)
00116   {
00117     Init(aPresContext, aTableFrame, aReason, aAvailWidth, aAvailHeight);
00118   }
00119 
00120   void Init(nsPresContext& aPresContext,
00121             nsTableFrame&   aTableFrame,
00122             nsReflowReason  aReason,
00123             nscoord         aAvailWidth,
00124             nscoord         aAvailHeight)
00125   {
00126     reason = aReason;
00127 
00128     nsTableFrame* table = (nsTableFrame*)aTableFrame.GetFirstInFlow();
00129     nsMargin borderPadding = table->GetChildAreaOffset(&reflowState);
00130     nscoord cellSpacingX = table->GetCellSpacingX();
00131 
00132     x = borderPadding.left + cellSpacingX;
00133     y = borderPadding.top; //cellspacing added during reflow
00134 
00135     availSize.width  = aAvailWidth;
00136     if (NS_UNCONSTRAINEDSIZE != availSize.width) {
00137       availSize.width -= borderPadding.left + borderPadding.right
00138                          + (2 * cellSpacingX);
00139       availSize.width = PR_MAX(0, availSize.width);
00140     }
00141 
00142     availSize.height = aAvailHeight;
00143     if (NS_UNCONSTRAINEDSIZE != availSize.height) {
00144       availSize.height -= borderPadding.top + borderPadding.bottom
00145                           + (2 * table->GetCellSpacingY());
00146       availSize.height = PR_MAX(0, availSize.height);
00147     }
00148 
00149     footerFrame      = nsnull;
00150     firstBodySection = nsnull;
00151   }
00152 
00153   nsTableReflowState(nsPresContext&          aPresContext,
00154                      const nsHTMLReflowState& aReflowState,
00155                      nsTableFrame&            aTableFrame)
00156     : reflowState(aReflowState)
00157   {
00158     Init(aPresContext, aTableFrame, aReflowState.reason, aReflowState.availableWidth, aReflowState.availableHeight);
00159   }
00160 
00161 };
00162 
00163 /********************************************************************************
00164  ** nsTableFrame                                                               **
00165  ********************************************************************************/
00166 #if defined DEBUG_TABLE_REFLOW_TIMING
00167 static PRInt32 gRflCount = 0;
00168 #endif
00169 
00170 struct BCPropertyData
00171 {
00172   BCPropertyData() { mDamageArea.x = mDamageArea.y = mDamageArea.width = mDamageArea.height =
00173                      mTopBorderWidth = mRightBorderWidth = mBottomBorderWidth = mLeftBorderWidth = 0; }
00174   nsRect  mDamageArea;
00175   BCPixelSize mTopBorderWidth;
00176   BCPixelSize mRightBorderWidth;
00177   BCPixelSize mBottomBorderWidth;
00178   BCPixelSize mLeftBorderWidth;
00179 };
00180 
00181 NS_IMETHODIMP 
00182 nsTableFrame::GetParentStyleContextFrame(nsPresContext* aPresContext,
00183                                          nsIFrame**      aProviderFrame,
00184                                          PRBool*         aIsChild)
00185 {
00186   // Since our parent, the table outer frame, returned this frame, we
00187   // must return whatever our parent would normally have returned.
00188 
00189   NS_PRECONDITION(mParent, "table constructed without outer table");
00190   return NS_STATIC_CAST(nsFrame*, mParent)->
00191           DoGetParentStyleContextFrame(aPresContext, aProviderFrame, aIsChild);
00192 }
00193 
00194 
00195 nsIAtom*
00196 nsTableFrame::GetType() const
00197 {
00198   return nsLayoutAtoms::tableFrame; 
00199 }
00200 
00201 
00202 nsTableFrame::nsTableFrame()
00203   : nsHTMLContainerFrame(),
00204     mCellMap(nsnull),
00205     mTableLayoutStrategy(nsnull),
00206     mPreferredWidth(0)
00207 {
00208   mBits.mHadInitialReflow       = PR_FALSE;
00209   mBits.mHaveReflowedColGroups  = PR_FALSE;
00210   mBits.mNeedStrategyInit       = PR_TRUE;
00211   mBits.mNeedStrategyBalance    = PR_TRUE;
00212   mBits.mCellSpansPctCol        = PR_FALSE;
00213   mBits.mNeedToCalcBCBorders    = PR_FALSE;
00214   mBits.mIsBorderCollapse       = PR_FALSE;
00215 
00216 #ifdef DEBUG_TABLE_REFLOW_TIMING
00217   mTimer = new nsReflowTimer(this);
00218   nsReflowTimer* timer = new nsReflowTimer(this);
00219   mTimer->mNextSibling = timer;
00220   timer = new nsReflowTimer(this);
00221   mTimer->mNextSibling->mNextSibling = timer;
00222   timer = new nsReflowTimer(this);
00223   mTimer->mNextSibling->mNextSibling->mNextSibling = timer;
00224   timer = new nsReflowTimer(this);
00225   mTimer->mNextSibling->mNextSibling->mNextSibling->mNextSibling = timer;
00226   timer = new nsReflowTimer(this);
00227   mTimer->mNextSibling->mNextSibling->mNextSibling->mNextSibling->mNextSibling = timer;
00228 #endif
00229 }
00230 
00231 NS_IMPL_ADDREF_INHERITED(nsTableFrame, nsHTMLContainerFrame)
00232 NS_IMPL_RELEASE_INHERITED(nsTableFrame, nsHTMLContainerFrame)
00233 
00234 nsresult nsTableFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
00235 {
00236   if (NULL == aInstancePtr) {
00237     return NS_ERROR_NULL_POINTER;
00238   }
00239   if (aIID.Equals(NS_GET_IID(nsITableLayout))) 
00240   { // note there is no addref here, frames are not addref'd
00241     *aInstancePtr = (void*)(nsITableLayout*)this;
00242     return NS_OK;
00243   }
00244   else {
00245     return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
00246   }
00247 }
00248 
00249 NS_IMETHODIMP
00250 nsTableFrame::Init(nsPresContext*  aPresContext,
00251                    nsIContent*      aContent,
00252                    nsIFrame*        aParent,
00253                    nsStyleContext*  aContext,
00254                    nsIFrame*        aPrevInFlow)
00255 {
00256   nsresult  rv;
00257 
00258   // Let the base class do its processing
00259   rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext,
00260                                   aPrevInFlow);
00261 
00262   // record that children that are ignorable whitespace should be excluded 
00263   mState |= NS_FRAME_EXCLUDE_IGNORABLE_WHITESPACE;
00264 
00265   // see if border collapse is on, if so set it
00266   const nsStyleTableBorder* tableStyle = GetStyleTableBorder();
00267   PRBool borderCollapse = (NS_STYLE_BORDER_COLLAPSE == tableStyle->mBorderCollapse);
00268   SetBorderCollapse(borderCollapse);
00269   // Create the cell map
00270   // XXX Why do we do this for continuing frames?
00271   mCellMap = new nsTableCellMap(*this, borderCollapse);
00272   if (!mCellMap) return NS_ERROR_OUT_OF_MEMORY;
00273 
00274   if (aPrevInFlow) {
00275     // set my width, because all frames in a table flow are the same width and
00276     // code in nsTableOuterFrame depends on this being set
00277     mRect.width = aPrevInFlow->GetSize().width;
00278   }
00279   else {
00280     NS_ASSERTION(!mTableLayoutStrategy, "strategy was created before Init was called");
00281     // create the strategy
00282     mTableLayoutStrategy = (IsAutoLayout()) ?
00283       new BasicTableLayoutStrategy(this,
00284                 eCompatibility_NavQuirks == aPresContext->CompatibilityMode())
00285       : new FixedTableLayoutStrategy(this);
00286   }
00287 
00288   return rv;
00289 }
00290 
00291 
00292 nsTableFrame::~nsTableFrame()
00293 {
00294   if (nsnull!=mCellMap) {
00295     delete mCellMap; 
00296     mCellMap = nsnull;
00297   }
00298 
00299   if (nsnull!=mTableLayoutStrategy) {
00300     delete mTableLayoutStrategy;
00301     mTableLayoutStrategy = nsnull;
00302   }
00303 #ifdef DEBUG_TABLE_REFLOW_TIMING
00304   nsTableFrame::DebugReflowDone(this);
00305 #endif
00306 }
00307 
00308 NS_IMETHODIMP
00309 nsTableFrame::Destroy(nsPresContext* aPresContext)
00310 {
00311   mColGroups.DestroyFrames(aPresContext);
00312   return nsHTMLContainerFrame::Destroy(aPresContext);
00313 }
00314 
00315 nscoord 
00316 nsTableFrame::RoundToPixel(nscoord       aValue,
00317                            float         aPixelToTwips,
00318                            nsPixelRound  aRound)
00319 {
00320   nscoord fullPixel = NSToCoordRound(aPixelToTwips);
00321   if (fullPixel <= 0) 
00322     // We must be rendering to a device that has a resolution greater than Twips! 
00323     // In that case, aValue is as accurate as it's going to get.
00324     return aValue;
00325   
00326   PRInt32 excess = aValue % fullPixel;
00327   if (0 == excess) 
00328     return aValue;
00329 
00330   nscoord halfPixel = NSToCoordRound(aPixelToTwips / 2.0f);
00331   switch(aRound) {
00332   case eRoundUpIfHalfOrMore:
00333     if (excess >= halfPixel) { // eRoundUpIfHalfOrMore
00334       return aValue + (fullPixel - excess);
00335     }
00336   case eAlwaysRoundDown:
00337     return aValue - excess;
00338   default: // eAlwaysRoundUp
00339     return aValue + (fullPixel - excess);
00340   }
00341 }
00342 
00343 // Helper function which marks aFrame as dirty and generates a reflow command
00344 nsresult
00345 nsTableFrame::AppendDirtyReflowCommand(nsIFrame* aFrame)
00346 {
00347   aFrame->AddStateBits(NS_FRAME_IS_DIRTY);  // mark the table frame as dirty
00348 
00349   return aFrame->GetPresContext()->PresShell()->
00350           AppendReflowCommand(aFrame, eReflowType_ReflowDirty, nsnull);
00351 }
00352 
00353 // Make sure any views are positioned properly
00354 void
00355 nsTableFrame::RePositionViews(nsIFrame* aFrame)
00356 {
00357   nsContainerFrame::PositionFrameView(aFrame);
00358   nsContainerFrame::PositionChildViews(aFrame);
00359 }
00360 
00361 static PRBool
00362 IsRepeatedFrame(nsIFrame* kidFrame)
00363 {
00364   return (kidFrame->GetType() == nsLayoutAtoms::tableRowFrame ||
00365           kidFrame->GetType() == nsLayoutAtoms::tableRowGroupFrame) &&
00366          (kidFrame->GetStateBits() & NS_REPEATED_ROW_OR_ROWGROUP);
00367 }
00368 
00369 PRBool
00370 nsTableFrame::PageBreakAfter(nsIFrame& aSourceFrame,
00371                              nsIFrame* aNextFrame)
00372 {
00373   const nsStyleDisplay* display = aSourceFrame.GetStyleDisplay();
00374   // don't allow a page break after a repeated element ...
00375   if (display->mBreakAfter && !IsRepeatedFrame(&aSourceFrame)) {
00376     return !(aNextFrame && IsRepeatedFrame(aNextFrame)); // or before
00377   }
00378 
00379   if (aNextFrame) {
00380     display = aNextFrame->GetStyleDisplay();
00381     // don't allow a page break before a repeated element ...
00382     if (display->mBreakBefore && !IsRepeatedFrame(aNextFrame)) {
00383       return !IsRepeatedFrame(&aSourceFrame); // or after
00384     }
00385   }
00386   return PR_FALSE;
00387 }
00388 
00389 // XXX this needs to be cleaned up so that the frame constructor breaks out col group
00390 // frames into a separate child list.
00391 NS_IMETHODIMP
00392 nsTableFrame::SetInitialChildList(nsPresContext* aPresContext,
00393                                   nsIAtom*        aListName,
00394                                   nsIFrame*       aChildList)
00395 {
00396   nsresult rv=NS_OK;
00397 
00398   // I know now that I have all my children, so build the cell map
00399   nsIFrame *childFrame = aChildList;
00400   nsIFrame *prevMainChild = nsnull;
00401   nsIFrame *prevColGroupChild = nsnull;
00402   for ( ; nsnull!=childFrame; )
00403   {
00404     const nsStyleDisplay* childDisplay = childFrame->GetStyleDisplay();
00405     // XXX this if should go away
00406     if (PR_TRUE==IsRowGroup(childDisplay->mDisplay))
00407     {
00408       if (mFrames.IsEmpty()) 
00409         mFrames.SetFrames(childFrame);
00410       else
00411         prevMainChild->SetNextSibling(childFrame);
00412       prevMainChild = childFrame;
00413     }
00414     else if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == childDisplay->mDisplay)
00415     {
00416       NS_ASSERTION(nsLayoutAtoms::tableColGroupFrame == childFrame->GetType(),
00417                    "This is not a colgroup");
00418       if (mColGroups.IsEmpty())
00419         mColGroups.SetFrames(childFrame);
00420       else
00421         prevColGroupChild->SetNextSibling(childFrame);
00422       prevColGroupChild = childFrame;
00423     }
00424     else
00425     { // unknown frames go on the main list for now
00426       if (mFrames.IsEmpty())
00427         mFrames.SetFrames(childFrame);
00428       else
00429         prevMainChild->SetNextSibling(childFrame);
00430       prevMainChild = childFrame;
00431     }
00432     nsIFrame *prevChild = childFrame;
00433     childFrame = childFrame->GetNextSibling();
00434     prevChild->SetNextSibling(nsnull);
00435   }
00436   if (nsnull!=prevMainChild)
00437     prevMainChild->SetNextSibling(nsnull);
00438   if (nsnull!=prevColGroupChild)
00439     prevColGroupChild->SetNextSibling(nsnull);
00440 
00441   // If we have a prev-in-flow, then we're a table that has been split and
00442   // so don't treat this like an append
00443   if (!mPrevInFlow) {
00444     // process col groups first so that real cols get constructed before
00445     // anonymous ones due to cells in rows.
00446     InsertColGroups(0, mColGroups.FirstChild());
00447     AppendRowGroups(mFrames.FirstChild());
00448     // calc collapsing borders if this is the default (row group, col group, child list)
00449     if (!aChildList && IsBorderCollapse()) {
00450       nsRect damageArea(0, 0, GetColCount(), GetRowCount());
00451       SetBCDamageArea(damageArea);
00452     }
00453   }
00454 
00455   return rv;
00456 }
00457 
00458 /* virtual */ PRBool
00459 nsTableFrame::IsContainingBlock() const
00460 {
00461   return PR_TRUE;
00462 }
00463 
00464 void nsTableFrame::AttributeChangedFor(nsIFrame*       aFrame,
00465                                        nsIContent*     aContent, 
00466                                        nsIAtom*        aAttribute)
00467 {
00468   if (IS_TABLE_CELL(aFrame->GetType())) {
00469     if ((nsHTMLAtoms::rowspan == aAttribute) || 
00470         (nsHTMLAtoms::colspan == aAttribute)) {
00471       nsTableCellMap* cellMap = GetCellMap();
00472       if (cellMap) {
00473         // for now just remove the cell from the map and reinsert it
00474         nsTableCellFrame* cellFrame = (nsTableCellFrame*)aFrame;
00475         PRInt32 rowIndex, colIndex;
00476         cellFrame->GetRowIndex(rowIndex);
00477         cellFrame->GetColIndex(colIndex);
00478         RemoveCell(cellFrame, rowIndex);
00479         nsAutoVoidArray cells;
00480         cells.AppendElement(cellFrame);
00481         InsertCells(cells, rowIndex, colIndex - 1);
00482 
00483         // XXX This could probably be optimized with some effort
00484         SetNeedStrategyInit(PR_TRUE);
00485         AppendDirtyReflowCommand(this);
00486       }
00487     }
00488   }
00489 }
00490 
00491 
00492 /* ****** CellMap methods ******* */
00493 
00494 PRInt32 nsTableFrame::GetRowCount () const
00495 {
00496   PRInt32 rowCount = 0;
00497   nsTableCellMap *cellMap = GetCellMap();
00498   NS_ASSERTION(nsnull!=cellMap, "GetRowCount null cellmap");
00499   if (nsnull!=cellMap)
00500     rowCount = cellMap->GetRowCount();
00501   return rowCount;
00502 }
00503 
00504 /* return the col count including dead cols */
00505 PRInt32 nsTableFrame::GetColCount () const
00506 {
00507   PRInt32 colCount = 0;
00508   nsTableCellMap* cellMap = GetCellMap();
00509   NS_ASSERTION(nsnull != cellMap, "GetColCount null cellmap");
00510   if (nsnull != cellMap) {
00511     colCount = cellMap->GetColCount();
00512   }
00513   return colCount;
00514 }
00515 
00516 /* return the effective col count */
00517 PRInt32 nsTableFrame::GetEffectiveColCount() const
00518 {
00519   PRInt32 colCount = GetColCount();
00520   // don't count cols at the end that don't have originating cells
00521   for (PRInt32 colX = colCount - 1; colX >= 0; colX--) {
00522     if (GetNumCellsOriginatingInCol(colX) <= 0) { 
00523       colCount--;
00524     }
00525     else break;
00526   }
00527   return colCount;
00528 }
00529 
00530 PRInt32 nsTableFrame::GetIndexOfLastRealCol()
00531 {
00532   PRInt32 numCols = mColFrames.Count();
00533   for (PRInt32 colX = numCols; colX >= 0; colX--) { 
00534     nsTableColFrame* colFrame = GetColFrame(colX);
00535     if (colFrame) {
00536       if (eColAnonymousCell != colFrame->GetColType()) {
00537         return colX;
00538       }
00539     }
00540   }
00541   return -1; 
00542 }
00543 
00544 nsTableColFrame*
00545 nsTableFrame::GetColFrame(PRInt32 aColIndex) const
00546 {
00547   NS_ASSERTION(!mPrevInFlow, "GetColFrame called on next in flow");
00548   PRInt32 numCols = mColFrames.Count();
00549   if ((aColIndex >= 0) && (aColIndex < numCols)) {
00550     return (nsTableColFrame *)mColFrames.ElementAt(aColIndex);
00551   }
00552   else {
00553     //NS_ASSERTION(PR_FALSE, "invalid col index");
00554     return nsnull;
00555   }
00556 }
00557 
00558 // can return nsnull
00559 nsTableCellFrame* nsTableFrame::GetCellFrameAt(PRInt32 aRowIndex, PRInt32 aColIndex)
00560 {
00561   nsTableCellMap* cellMap = GetCellMap();
00562   if (cellMap) 
00563     return cellMap->GetCellInfoAt(aRowIndex, aColIndex);
00564   return nsnull;
00565 }
00566 
00567 
00568 PRInt32 nsTableFrame::GetEffectiveRowSpan(PRInt32                 aRowIndex,
00569                                           const nsTableCellFrame& aCell) const
00570 {
00571   nsTableCellMap* cellMap = GetCellMap();
00572   NS_PRECONDITION (nsnull != cellMap, "bad call, cellMap not yet allocated.");
00573 
00574   PRInt32 colIndex;
00575   aCell.GetColIndex(colIndex);
00576   return cellMap->GetEffectiveRowSpan(aRowIndex, colIndex);
00577 }
00578 
00579 PRInt32 nsTableFrame::GetEffectiveRowSpan(const nsTableCellFrame& aCell,
00580                                           nsCellMap*              aCellMap)
00581 {
00582   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
00583 
00584   PRInt32 colIndex, rowIndex;
00585   aCell.GetColIndex(colIndex);
00586   aCell.GetRowIndex(rowIndex);
00587   PRBool ignore;
00588 
00589   if (aCellMap) 
00590     return aCellMap->GetRowSpan(*tableCellMap, rowIndex, colIndex, PR_TRUE, ignore);
00591   else
00592     return tableCellMap->GetEffectiveRowSpan(rowIndex, colIndex);
00593 }
00594 
00595 PRInt32 nsTableFrame::GetEffectiveColSpan(const nsTableCellFrame& aCell,
00596                                           nsCellMap*              aCellMap) const
00597 {
00598   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
00599 
00600   PRInt32 colIndex, rowIndex;
00601   aCell.GetColIndex(colIndex);
00602   aCell.GetRowIndex(rowIndex);
00603   PRBool ignore;
00604 
00605   if (aCellMap) 
00606     return aCellMap->GetEffectiveColSpan(*tableCellMap, rowIndex, colIndex, ignore);
00607   else
00608     return tableCellMap->GetEffectiveColSpan(rowIndex, colIndex);
00609 }
00610 
00611 PRBool nsTableFrame::HasMoreThanOneCell(PRInt32 aRowIndex) const
00612 {
00613   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT1(1);
00614   return tableCellMap->HasMoreThanOneCell(aRowIndex);
00615 }
00616 
00617 PRInt32 nsTableFrame::GetEffectiveCOLSAttribute()
00618 {
00619   NS_PRECONDITION (GetCellMap(), "null cellMap.");
00620 
00621   PRInt32 result;
00622   result = GetStyleTable()->mCols;
00623   PRInt32 numCols = GetColCount();
00624   if (result > numCols)
00625     result = numCols;
00626   return result;
00627 }
00628 
00629 void nsTableFrame::AdjustRowIndices(PRInt32         aRowIndex,
00630                                     PRInt32         aAdjustment)
00631 {
00632   // Iterate over the row groups and adjust the row indices of all rows 
00633   // whose index is >= aRowIndex.
00634   nsAutoVoidArray rowGroups;
00635   PRUint32 numRowGroups;
00636   OrderRowGroups(rowGroups, numRowGroups, nsnull);
00637 
00638   for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
00639     nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
00640     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
00641     rgFrame->AdjustRowIndices(aRowIndex, aAdjustment);
00642   }
00643 }
00644 
00645 
00646 void nsTableFrame::ResetRowIndices(nsIFrame* aFirstRowGroupFrame,
00647                                    nsIFrame* aLastRowGroupFrame)
00648 {
00649   // Iterate over the row groups and adjust the row indices of all rows
00650   // omit the rowgroups that will be inserted later
00651   nsAutoVoidArray rowGroups;
00652   PRUint32 numRowGroups;
00653   OrderRowGroups(rowGroups, numRowGroups, nsnull);
00654 
00655   PRInt32 rowIndex = 0;
00656   nsTableRowGroupFrame* newRgFrame = nsnull;
00657   nsIFrame* omitRgFrame = aFirstRowGroupFrame;
00658   if (omitRgFrame) {
00659     newRgFrame = GetRowGroupFrame(omitRgFrame);
00660     if (omitRgFrame == aLastRowGroupFrame)
00661       omitRgFrame = nsnull;
00662   }
00663 
00664   for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
00665     nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
00666     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
00667     if (rgFrame == newRgFrame) {
00668       // omit the new rowgroup
00669       if (omitRgFrame) {
00670         omitRgFrame = omitRgFrame->GetNextSibling();
00671         if (omitRgFrame) {
00672           newRgFrame  = GetRowGroupFrame(omitRgFrame);
00673           if (omitRgFrame == aLastRowGroupFrame)
00674             omitRgFrame = nsnull;
00675         }
00676       }
00677     }
00678     else {
00679       nsIFrame* rowFrame = rgFrame->GetFirstChild(nsnull);
00680       for ( ; rowFrame; rowFrame = rowFrame->GetNextSibling()) {
00681         if (NS_STYLE_DISPLAY_TABLE_ROW==rowFrame->GetStyleDisplay()->mDisplay) {
00682           ((nsTableRowFrame *)rowFrame)->SetRowIndex(rowIndex);
00683           rowIndex++;
00684         }
00685       }
00686     }
00687   }
00688 }
00689 void nsTableFrame::InsertColGroups(PRInt32         aStartColIndex,
00690                                    nsIFrame*       aFirstFrame,
00691                                    nsIFrame*       aLastFrame)
00692 {
00693   PRInt32 colIndex = aStartColIndex;
00694   nsTableColGroupFrame* firstColGroupToReset = nsnull;
00695   nsIFrame* kidFrame = aFirstFrame;
00696   PRBool didLastFrame = PR_FALSE;
00697   while (kidFrame) {
00698     if (nsLayoutAtoms::tableColGroupFrame == kidFrame->GetType()) {
00699       if (didLastFrame) {
00700         firstColGroupToReset = (nsTableColGroupFrame*)kidFrame;
00701         break;
00702       }
00703       else {
00704         nsTableColGroupFrame* cgFrame = (nsTableColGroupFrame*)kidFrame;
00705         cgFrame->SetStartColumnIndex(colIndex);
00706         nsIFrame* firstCol = kidFrame->GetFirstChild(nsnull);
00707         cgFrame->AddColsToTable(colIndex, PR_FALSE, firstCol);
00708         PRInt32 numCols = cgFrame->GetColCount();
00709         colIndex += numCols;
00710       }
00711     }
00712     if (kidFrame == aLastFrame) {
00713       didLastFrame = PR_TRUE;
00714     }
00715     kidFrame = kidFrame->GetNextSibling();
00716   }
00717 
00718   if (firstColGroupToReset) {
00719     nsTableColGroupFrame::ResetColIndices(firstColGroupToReset, colIndex);
00720   }
00721 }
00722 
00723 void nsTableFrame::InsertCol(nsTableColFrame& aColFrame,
00724                              PRInt32          aColIndex)
00725 {
00726   mColFrames.InsertElementAt(&aColFrame, aColIndex);
00727   nsTableColType insertedColType = aColFrame.GetColType();
00728   PRInt32 numCacheCols = mColFrames.Count();
00729   nsTableCellMap* cellMap = GetCellMap();
00730   if (cellMap) {
00731     PRInt32 numMapCols = cellMap->GetColCount();
00732     if (numCacheCols > numMapCols) {
00733       PRBool removedFromCache = PR_FALSE;
00734       if (eColAnonymousCell != insertedColType) {
00735         nsTableColFrame* lastCol = (nsTableColFrame *)mColFrames.ElementAt(numCacheCols - 1);
00736         if (lastCol) {
00737           nsTableColType lastColType = lastCol->GetColType();
00738           if (eColAnonymousCell == lastColType) {
00739             // remove the col from the cache
00740             mColFrames.RemoveElementAt(numCacheCols - 1);
00741             // remove the col from the eColGroupAnonymousCell col group
00742             nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild();
00743             if (lastColGroup) {
00744               lastColGroup->RemoveChild(*lastCol, PR_FALSE);
00745             }
00746             // remove the col group if it is empty
00747             if (lastColGroup->GetColCount() <= 0) {
00748               mColGroups.DestroyFrame(GetPresContext(), (nsIFrame*)lastColGroup);
00749             }
00750             removedFromCache = PR_TRUE;
00751           }
00752         }
00753       }
00754       if (!removedFromCache) {
00755         cellMap->AddColsAtEnd(1);
00756       }
00757     }
00758   }
00759   // for now, just bail and recalc all of the collapsing borders
00760   if (IsBorderCollapse()) {
00761     nsRect damageArea(0, 0, PR_MAX(1, GetColCount()), PR_MAX(1, GetRowCount()));
00762     SetBCDamageArea(damageArea);
00763   }
00764 }
00765 
00766 void nsTableFrame::RemoveCol(nsTableColGroupFrame* aColGroupFrame,
00767                              PRInt32               aColIndex,
00768                              PRBool                aRemoveFromCache,
00769                              PRBool                aRemoveFromCellMap)
00770 {
00771   if (aRemoveFromCache) {
00772     mColFrames.RemoveElementAt(aColIndex);
00773   }
00774   if (aRemoveFromCellMap) {
00775     nsTableCellMap* cellMap = GetCellMap();
00776     if (cellMap) {
00777       CreateAnonymousColFrames(1, eColAnonymousCell, PR_TRUE);
00778     }
00779   }
00780   // for now, just bail and recalc all of the collapsing borders
00781   if (IsBorderCollapse()) {
00782     nsRect damageArea(0, 0, GetColCount(), GetRowCount());
00783     SetBCDamageArea(damageArea);
00784   }
00785 }
00786 
00790 nsTableCellMap* nsTableFrame::GetCellMap() const
00791 {
00792   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
00793   if (this == firstInFlow) {
00794     return mCellMap;
00795   }
00796   else {
00797     return firstInFlow->GetCellMap();
00798   }
00799 }
00800 
00801 nscoord nsTableFrame::GetMinWidth() const
00802 {
00803   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
00804   if (this == firstInFlow) {
00805     return mMinWidth;
00806   }
00807   else {
00808     return firstInFlow->GetMinWidth();
00809   }
00810 }
00811 
00812 nscoord nsTableFrame::GetDesiredWidth() const
00813 {
00814   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
00815   if (this == firstInFlow) {
00816     return mDesiredWidth;
00817   }
00818   else {
00819     return firstInFlow->GetDesiredWidth();
00820   }
00821 }
00822 
00823 nscoord nsTableFrame::GetPreferredWidth() const
00824 {
00825   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
00826   if (this == firstInFlow) {
00827     return mPreferredWidth;
00828   }
00829   else {
00830     return firstInFlow->GetPreferredWidth();
00831   }
00832 }
00833 
00834 // XXX this needs to be moved to nsCSSFrameConstructor
00835 nsTableColGroupFrame*
00836 nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType)
00837 {
00838   nsIContent* colGroupContent = GetContent();
00839   nsPresContext* presContext = GetPresContext();
00840   nsIPresShell *shell = presContext->PresShell();
00841 
00842   nsRefPtr<nsStyleContext> colGroupStyle;
00843   colGroupStyle = shell->StyleSet()->ResolvePseudoStyleFor(colGroupContent,
00844                                                            nsCSSAnonBoxes::tableColGroup,
00845                                                            mStyleContext);
00846   // Create a col group frame
00847   nsIFrame* newFrame;
00848   nsresult result = NS_NewTableColGroupFrame(shell, &newFrame);
00849   if (NS_SUCCEEDED(result) && newFrame) {
00850     ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType);
00851     newFrame->Init(presContext, colGroupContent, this, colGroupStyle, nsnull);
00852   }
00853   return (nsTableColGroupFrame *)newFrame;
00854 }
00855 
00856 void
00857 nsTableFrame::CreateAnonymousColFrames(PRInt32         aNumColsToAdd,
00858                                        nsTableColType  aColType,
00859                                        PRBool          aDoAppend,
00860                                        nsIFrame*       aPrevColIn)
00861 {
00862   // get the last col group frame
00863   nsTableColGroupFrame* colGroupFrame = nsnull;
00864   nsIFrame* childFrame = mColGroups.FirstChild();
00865   while (childFrame) {
00866     if (nsLayoutAtoms::tableColGroupFrame == childFrame->GetType()) {
00867       colGroupFrame = (nsTableColGroupFrame *)childFrame;
00868     }
00869     childFrame = childFrame->GetNextSibling();
00870   }
00871 
00872   nsTableColGroupType lastColGroupType = eColGroupContent; 
00873   nsTableColGroupType newColGroupType  = eColGroupContent; 
00874   if (colGroupFrame) {
00875     lastColGroupType = colGroupFrame->GetColType();
00876   }
00877   if (eColAnonymousCell == aColType) {
00878     if (eColGroupAnonymousCell != lastColGroupType) {
00879       newColGroupType = eColGroupAnonymousCell;
00880     }
00881   }
00882   else if (eColAnonymousCol == aColType) {
00883     if (eColGroupAnonymousCol != lastColGroupType) {
00884       newColGroupType = eColGroupAnonymousCol;
00885     }
00886   }
00887   else {
00888     NS_ASSERTION(PR_FALSE, "CreateAnonymousColFrames called incorrectly");
00889     return;
00890   }
00891 
00892   if (eColGroupContent != newColGroupType) {
00893     PRInt32 colIndex = (colGroupFrame) ? colGroupFrame->GetStartColumnIndex() + colGroupFrame->GetColCount()
00894                                        : 0;
00895     colGroupFrame = CreateAnonymousColGroupFrame(newColGroupType);
00896     if (!colGroupFrame) {
00897       return;
00898     }
00899     mColGroups.AppendFrame(this, colGroupFrame); // add the new frame to the child list
00900     colGroupFrame->SetStartColumnIndex(colIndex);
00901   }
00902 
00903   nsIFrame* prevCol = (aDoAppend) ? colGroupFrame->GetChildList().LastChild() : aPrevColIn;
00904 
00905   nsIFrame* firstNewFrame;
00906   CreateAnonymousColFrames(colGroupFrame, aNumColsToAdd, aColType,
00907                            PR_TRUE, prevCol, &firstNewFrame);
00908 }
00909 
00910 // XXX this needs to be moved to nsCSSFrameConstructor
00911 // Right now it only creates the col frames at the end 
00912 void
00913 nsTableFrame::CreateAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame,
00914                                        PRInt32               aNumColsToAdd,
00915                                        nsTableColType        aColType,
00916                                        PRBool                aAddToColGroupAndTable,         
00917                                        nsIFrame*             aPrevFrameIn,
00918                                        nsIFrame**            aFirstNewFrame)
00919 {
00920   NS_PRECONDITION(aColGroupFrame, "null frame");
00921   *aFirstNewFrame = nsnull;
00922   nsIFrame* lastColFrame = nsnull;
00923   nsPresContext* presContext = GetPresContext();
00924   nsIPresShell *shell = presContext->PresShell();
00925 
00926   // Get the last col frame
00927   nsIFrame* childFrame = aColGroupFrame->GetFirstChild(nsnull);
00928   while (childFrame) {
00929     if (nsLayoutAtoms::tableColFrame == childFrame->GetType()) {
00930       lastColFrame = (nsTableColGroupFrame *)childFrame;
00931     }
00932     childFrame = childFrame->GetNextSibling();
00933   }
00934 
00935   PRInt32 startIndex = mColFrames.Count();
00936   PRInt32 lastIndex  = startIndex + aNumColsToAdd - 1; 
00937 
00938   for (PRInt32 childX = startIndex; childX <= lastIndex; childX++) {
00939     nsIContent* iContent;
00940     nsRefPtr<nsStyleContext> styleContext;
00941     nsStyleContext* parentStyleContext;
00942 
00943     if ((aColType == eColAnonymousCol) && aPrevFrameIn) {
00944       // a col due to a span in a previous col uses the style context of the col
00945       styleContext = aPrevFrameIn->GetStyleContext();
00946       // fix for bugzilla bug 54454: get the content from the prevFrame 
00947       iContent = aPrevFrameIn->GetContent();
00948     }
00949     else {
00950       // all other anonymous cols use a pseudo style context of the col group
00951       iContent = aColGroupFrame->GetContent();
00952       parentStyleContext = aColGroupFrame->GetStyleContext();
00953       styleContext = shell->StyleSet()->ResolvePseudoStyleFor(iContent,
00954                                                               nsCSSAnonBoxes::tableCol,
00955                                                               parentStyleContext);
00956     }
00957     // ASSERTION to check for bug 54454 sneaking back in...
00958     NS_ASSERTION(iContent, "null content in CreateAnonymousColFrames");
00959 
00960     // create the new col frame
00961     nsIFrame* colFrame;
00962     NS_NewTableColFrame(shell, &colFrame);
00963     ((nsTableColFrame *) colFrame)->SetColType(aColType);
00964     colFrame->Init(presContext, iContent, aColGroupFrame,
00965                    styleContext, nsnull);
00966     colFrame->SetInitialChildList(presContext, nsnull, nsnull);
00967 
00968     // Add the col to the sibling chain
00969     if (lastColFrame) {
00970       lastColFrame->SetNextSibling(colFrame);
00971     }
00972     lastColFrame = colFrame;
00973     if (childX == startIndex) {
00974       *aFirstNewFrame = colFrame;
00975     }
00976   }
00977   if (aAddToColGroupAndTable) {
00978     nsFrameList& cols = aColGroupFrame->GetChildList();
00979     // the chain already exists, now add it to the col group child list
00980     if (!aPrevFrameIn) {
00981       cols.AppendFrames(aColGroupFrame, *aFirstNewFrame);
00982     }
00983     // get the starting col index in the cache
00984     PRInt32 startColIndex = aColGroupFrame->GetStartColumnIndex();
00985     if (aPrevFrameIn) {
00986       nsTableColFrame* colFrame = 
00987         (nsTableColFrame*)nsTableFrame::GetFrameAtOrBefore((nsIFrame*) aColGroupFrame, aPrevFrameIn, 
00988                                                            nsLayoutAtoms::tableColFrame);
00989       if (colFrame) {
00990         startColIndex = colFrame->GetColIndex() + 1;
00991       }
00992     }
00993     aColGroupFrame->AddColsToTable(startColIndex, PR_TRUE, 
00994                                   *aFirstNewFrame, lastColFrame);
00995   }
00996 }
00997 
00998 void
00999 nsTableFrame::MatchCellMapToColCache(nsTableCellMap* aCellMap)
01000 {
01001   PRInt32 numColsInMap   = GetColCount();
01002   PRInt32 numColsInCache = mColFrames.Count();
01003   PRInt32 numColsToAdd = numColsInMap - numColsInCache;
01004   if (numColsToAdd > 0) {
01005     // this sets the child list, updates the col cache and cell map
01006     CreateAnonymousColFrames(numColsToAdd, eColAnonymousCell, PR_TRUE); 
01007   }
01008   if (numColsToAdd < 0) {
01009     PRInt32 numColsNotRemoved = DestroyAnonymousColFrames(-numColsToAdd);
01010     // if the cell map has fewer cols than the cache, correct it
01011     if (numColsNotRemoved > 0) {
01012       aCellMap->AddColsAtEnd(numColsNotRemoved);
01013     }
01014   }
01015 }
01016 void
01017 nsTableFrame::AppendCell(nsTableCellFrame& aCellFrame,
01018                          PRInt32           aRowIndex)
01019 {
01020   nsTableCellMap* cellMap = GetCellMap();
01021   if (cellMap) {
01022     nsRect damageArea(0,0,0,0);
01023     cellMap->AppendCell(aCellFrame, aRowIndex, PR_TRUE, damageArea);
01024     MatchCellMapToColCache(cellMap);
01025     if (IsBorderCollapse()) {
01026       SetBCDamageArea(damageArea);
01027     }
01028   }
01029 }
01030 
01031 void nsTableFrame::InsertCells(nsVoidArray&    aCellFrames, 
01032                                PRInt32         aRowIndex, 
01033                                PRInt32         aColIndexBefore)
01034 {
01035   nsTableCellMap* cellMap = GetCellMap();
01036   if (cellMap) {
01037     nsRect damageArea(0,0,0,0);
01038     cellMap->InsertCells(aCellFrames, aRowIndex, aColIndexBefore, damageArea);
01039     MatchCellMapToColCache(cellMap);
01040     if (IsBorderCollapse()) {
01041       SetBCDamageArea(damageArea);
01042     }
01043   }
01044 }
01045 
01046 // this removes the frames from the col group and table, but not the cell map
01047 PRInt32 
01048 nsTableFrame::DestroyAnonymousColFrames(PRInt32 aNumFrames)
01049 {
01050   // only remove cols that are of type eTypeAnonymous cell (they are at the end)
01051   PRInt32 endIndex   = mColFrames.Count() - 1;
01052   PRInt32 startIndex = (endIndex - aNumFrames) + 1;
01053   PRInt32 numColsRemoved = 0;
01054   for (PRInt32 colX = endIndex; colX >= startIndex; colX--) {
01055     nsTableColFrame* colFrame = GetColFrame(colX);
01056     if (colFrame && (eColAnonymousCell == colFrame->GetColType())) {
01057       nsTableColGroupFrame* cgFrame =
01058         NS_STATIC_CAST(nsTableColGroupFrame*, colFrame->GetParent());
01059       // remove the frame from the colgroup
01060       cgFrame->RemoveChild(*colFrame, PR_FALSE);
01061       // remove the frame from the cache, but not the cell map 
01062       RemoveCol(nsnull, colX, PR_TRUE, PR_FALSE);
01063       numColsRemoved++;
01064     }
01065     else {
01066       break; 
01067     }
01068   }
01069   return (aNumFrames - numColsRemoved);
01070 }
01071 
01072 void nsTableFrame::RemoveCell(nsTableCellFrame* aCellFrame,
01073                               PRInt32           aRowIndex)
01074 {
01075   nsTableCellMap* cellMap = GetCellMap();
01076   if (cellMap) {
01077     nsRect damageArea(0,0,0,0);
01078     cellMap->RemoveCell(aCellFrame, aRowIndex, damageArea);
01079     MatchCellMapToColCache(cellMap);
01080     if (IsBorderCollapse()) {
01081       SetBCDamageArea(damageArea);
01082     }
01083   }
01084 }
01085 
01086 PRInt32
01087 nsTableFrame::GetStartRowIndex(nsTableRowGroupFrame& aRowGroupFrame)
01088 {
01089   nsAutoVoidArray orderedRowGroups;
01090   PRUint32 numRowGroups;
01091   OrderRowGroups(orderedRowGroups, numRowGroups);
01092 
01093   PRInt32 rowIndex = 0;
01094   for (PRUint32 rgIndex = 0; rgIndex < numRowGroups; rgIndex++) {
01095     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex));
01096     if (rgFrame == &aRowGroupFrame) {
01097       break;
01098     }
01099     PRInt32 numRows = rgFrame->GetRowCount();
01100     rowIndex += numRows;
01101   }
01102   return rowIndex;
01103 }
01104 
01105 // this cannot extend beyond a single row group
01106 void nsTableFrame::AppendRows(nsTableRowGroupFrame& aRowGroupFrame,
01107                               PRInt32               aRowIndex,
01108                               nsVoidArray&          aRowFrames)
01109 {
01110   nsTableCellMap* cellMap = GetCellMap();
01111   if (cellMap) {
01112     PRInt32 absRowIndex = GetStartRowIndex(aRowGroupFrame) + aRowIndex;
01113     InsertRows(aRowGroupFrame, aRowFrames, absRowIndex, PR_TRUE);
01114   }
01115 }
01116 
01117 PRInt32
01118 nsTableFrame::InsertRow(nsTableRowGroupFrame& aRowGroupFrame,
01119                         nsIFrame&             aRowFrame,
01120                         PRInt32               aRowIndex,
01121                         PRBool                aConsiderSpans)
01122 {
01123   nsAutoVoidArray rows;
01124   rows.AppendElement(&aRowFrame);
01125   return InsertRows(aRowGroupFrame, rows, aRowIndex, aConsiderSpans);
01126 }
01127 
01128 // this cannot extend beyond a single row group
01129 PRInt32
01130 nsTableFrame::InsertRows(nsTableRowGroupFrame& aRowGroupFrame,
01131                          nsVoidArray&          aRowFrames,
01132                          PRInt32               aRowIndex,
01133                          PRBool                aConsiderSpans)
01134 {
01135 #ifdef DEBUG_TABLE_CELLMAP
01136   printf("=== insertRowsBefore firstRow=%d \n", aRowIndex);
01137   Dump(PR_TRUE, PR_FALSE, PR_TRUE);
01138 #endif
01139 
01140   PRInt32 numColsToAdd = 0;
01141   nsTableCellMap* cellMap = GetCellMap();
01142   if (cellMap) {
01143     nsRect damageArea(0,0,0,0);
01144     PRInt32 origNumRows = cellMap->GetRowCount();
01145     PRInt32 numNewRows = aRowFrames.Count();
01146     cellMap->InsertRows(aRowGroupFrame, aRowFrames, aRowIndex, aConsiderSpans, damageArea);
01147     MatchCellMapToColCache(cellMap);
01148     if (aRowIndex < origNumRows) {
01149       AdjustRowIndices(aRowIndex, numNewRows);
01150     }
01151     // assign the correct row indices to the new rows. If they were adjusted above
01152     // it may not have been done correctly because each row is constructed with index 0
01153     for (PRInt32 rowX = 0; rowX < numNewRows; rowX++) {
01154       nsTableRowFrame* rowFrame = (nsTableRowFrame *) aRowFrames.ElementAt(rowX);
01155       rowFrame->SetRowIndex(aRowIndex + rowX);
01156     }
01157     if (IsBorderCollapse()) {
01158       SetBCDamageArea(damageArea);
01159     }
01160   }
01161 #ifdef DEBUG_TABLE_CELLMAP
01162   printf("=== insertRowsAfter \n");
01163   Dump(PR_TRUE, PR_FALSE, PR_TRUE);
01164 #endif
01165 
01166   return numColsToAdd;
01167 }
01168 
01169 // this cannot extend beyond a single row group
01170 void nsTableFrame::RemoveRows(nsTableRowFrame& aFirstRowFrame,
01171                               PRInt32          aNumRowsToRemove,
01172                               PRBool           aConsiderSpans)
01173 {
01174 #ifdef TBD_OPTIMIZATION
01175   // decide if we need to rebalance. we have to do this here because the row group 
01176   // cannot do it when it gets the dirty reflow corresponding to the frame being destroyed
01177   PRBool stopTelling = PR_FALSE;
01178   for (nsIFrame* kidFrame = aFirstFrame.FirstChild(); (kidFrame && !stopAsking);
01179        kidFrame = kidFrame->GetNextSibling()) {
01180     if (IS_TABLE_CELL(kidFrame->GetType())) {
01181       nsTableCellFrame* cellFrame = (nsTableCellFrame*)kidFrame;
01182       stopTelling = tableFrame->CellChangedWidth(*cellFrame, cellFrame->GetPass1MaxElementWidth(), 
01183                                                  cellFrame->GetMaximumWidth(), PR_TRUE);
01184     }
01185   }
01186   // XXX need to consider what happens if there are cells that have rowspans 
01187   // into the deleted row. Need to consider moving rows if a rebalance doesn't happen
01188 #endif
01189 
01190   PRInt32 firstRowIndex = aFirstRowFrame.GetRowIndex();
01191 #ifdef DEBUG_TABLE_CELLMAP
01192   printf("=== removeRowsBefore firstRow=%d numRows=%d\n", firstRowIndex, aNumRowsToRemove);
01193   Dump(PR_TRUE, PR_FALSE, PR_TRUE);
01194 #endif
01195   nsTableCellMap* cellMap = GetCellMap();
01196   if (cellMap) {
01197     nsRect damageArea(0,0,0,0);
01198     cellMap->RemoveRows(firstRowIndex, aNumRowsToRemove, aConsiderSpans, damageArea);
01199     MatchCellMapToColCache(cellMap);
01200     if (IsBorderCollapse()) {
01201       SetBCDamageArea(damageArea);
01202     }
01203   }
01204   AdjustRowIndices(firstRowIndex, -aNumRowsToRemove);
01205 #ifdef DEBUG_TABLE_CELLMAP
01206   printf("=== removeRowsAfter\n");
01207   Dump(PR_TRUE, PR_TRUE, PR_TRUE);
01208 #endif
01209 }
01210 
01211 void nsTableFrame::AppendRowGroups(nsIFrame* aFirstRowGroupFrame)
01212 {
01213   if (aFirstRowGroupFrame) {
01214     nsTableCellMap* cellMap = GetCellMap();
01215     if (cellMap) {
01216       nsFrameList newList(aFirstRowGroupFrame);
01217       InsertRowGroups(aFirstRowGroupFrame, newList.LastChild());
01218     }
01219   }
01220 }
01221 
01222 nsTableRowGroupFrame*
01223 nsTableFrame::GetRowGroupFrame(nsIFrame* aFrame,
01224                                nsIAtom*  aFrameTypeIn)
01225 {
01226   nsIFrame* rgFrame = nsnull;
01227   nsIAtom* frameType = aFrameTypeIn;
01228   if (!aFrameTypeIn) {
01229     frameType = aFrame->GetType();
01230   }
01231   if (nsLayoutAtoms::tableRowGroupFrame == frameType) {
01232     rgFrame = aFrame;
01233   }
01234   else if (nsLayoutAtoms::scrollFrame == frameType) {
01235     nsIScrollableFrame* scrollable = nsnull;
01236     nsresult rv = CallQueryInterface(aFrame, &scrollable);
01237     if (NS_SUCCEEDED(rv) && (scrollable)) {
01238       nsIFrame* scrolledFrame = scrollable->GetScrolledFrame();
01239       if (scrolledFrame) {
01240         if (nsLayoutAtoms::tableRowGroupFrame == scrolledFrame->GetType()) {
01241           rgFrame = scrolledFrame;
01242         }
01243       }
01244     }
01245   }
01246   return (nsTableRowGroupFrame*)rgFrame;
01247 }
01248 
01249 // collect the rows ancestors of aFrame
01250 PRInt32
01251 nsTableFrame::CollectRows(nsIFrame*       aFrame,
01252                           nsVoidArray&    aCollection)
01253 {
01254   if (!aFrame) return 0;
01255   PRInt32 numRows = 0;
01256   nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aFrame);
01257   if (rgFrame) {
01258     nsIFrame* childFrame = rgFrame->GetFirstChild(nsnull);
01259     while (childFrame) {
01260       if (nsLayoutAtoms::tableRowFrame == childFrame->GetType()) {
01261         aCollection.AppendElement(childFrame);
01262         numRows++;
01263       }
01264       else {
01265         numRows += CollectRows(childFrame, aCollection);
01266       }
01267       childFrame = childFrame->GetNextSibling();
01268     }
01269   }
01270   return numRows;
01271 }
01272 
01273 void
01274 nsTableFrame::InsertRowGroups(nsIFrame* aFirstRowGroupFrame,
01275                               nsIFrame* aLastRowGroupFrame)
01276 {
01277 #ifdef DEBUG_TABLE_CELLMAP
01278   printf("=== insertRowGroupsBefore\n");
01279   Dump(PR_TRUE, PR_FALSE, PR_TRUE);
01280 #endif
01281   nsTableCellMap* cellMap = GetCellMap();
01282   if (cellMap) {
01283     nsAutoVoidArray orderedRowGroups;
01284     PRUint32 numRowGroups;
01285     OrderRowGroups(orderedRowGroups, numRowGroups);
01286     nsAutoVoidArray rows;
01287     // Loop over the rowgroups and check if some of them are new, if they are
01288     // insert cellmaps in the order that is predefined by OrderRowGroups,
01289     PRUint32 rgIndex; // declared here to avoid Win bustage
01290     for (rgIndex = 0; rgIndex < numRowGroups; rgIndex++) {
01291       nsIFrame* kidFrame = aFirstRowGroupFrame;
01292       while (kidFrame) {
01293         nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
01294 
01295         if (GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex)) == rgFrame) {
01296           nsTableRowGroupFrame* priorRG = (0 == rgIndex)
01297             ? nsnull : GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex - 1)); 
01298           // create and add the cell map for the row group
01299           cellMap->InsertGroupCellMap(*rgFrame, priorRG);
01300         
01301           break;
01302         }
01303         else {
01304           if (kidFrame == aLastRowGroupFrame) {
01305             break;
01306           }
01307           kidFrame = kidFrame->GetNextSibling();
01308         }
01309       }
01310     }
01311     cellMap->Synchronize(this);
01312     ResetRowIndices(aFirstRowGroupFrame, aLastRowGroupFrame);
01313 
01314     //now that the cellmaps are reordered too insert the rows
01315     for (rgIndex = 0; rgIndex < numRowGroups; rgIndex++) {
01316       nsIFrame* kidFrame = aFirstRowGroupFrame;
01317       while (kidFrame) {
01318         nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
01319 
01320         if (GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex)) == rgFrame) {
01321           nsTableRowGroupFrame* priorRG = (0 == rgIndex)
01322             ? nsnull : GetRowGroupFrame((nsIFrame*)orderedRowGroups.ElementAt(rgIndex - 1)); 
01323           // collect the new row frames in an array and add them to the table
01324           PRInt32 numRows = CollectRows(kidFrame, rows);
01325           if (numRows > 0) {
01326             PRInt32 rowIndex = 0;
01327             if (priorRG) {
01328               PRInt32 priorNumRows = priorRG->GetRowCount();
01329               rowIndex = priorRG->GetStartRowIndex() + priorNumRows;
01330             }
01331             InsertRows(*rgFrame, rows, rowIndex, PR_TRUE);
01332             rows.Clear();
01333           }
01334           break;
01335         }
01336         else {
01337           if (kidFrame == aLastRowGroupFrame) {
01338             break;
01339           }
01340           kidFrame = kidFrame->GetNextSibling();
01341         }
01342       }
01343     }    
01344     
01345   }
01346 #ifdef DEBUG_TABLE_CELLMAP
01347   printf("=== insertRowGroupsAfter\n");
01348   Dump(PR_TRUE, PR_TRUE, PR_TRUE);
01349 #endif
01350 }
01351 
01352 
01354 // Child frame enumeration
01355 
01356 nsIFrame*
01357 nsTableFrame::GetFirstChild(nsIAtom* aListName) const
01358 {
01359   if (aListName == nsLayoutAtoms::colGroupList) {
01360     return mColGroups.FirstChild();
01361   }
01362 
01363   return nsHTMLContainerFrame::GetFirstChild(aListName);
01364 }
01365 
01366 nsIAtom*
01367 nsTableFrame::GetAdditionalChildListName(PRInt32 aIndex) const
01368 {
01369   if (aIndex == NS_TABLE_FRAME_COLGROUP_LIST_INDEX) {
01370     return nsLayoutAtoms::colGroupList;
01371   }
01372   if (aIndex == NS_TABLE_FRAME_OVERFLOW_LIST_INDEX) {
01373     return nsLayoutAtoms::overflowList;
01374   } 
01375   return nsnull;
01376 }
01377 
01378 void 
01379 nsTableFrame::PaintChildren(nsPresContext*      aPresContext,
01380                             nsIRenderingContext& aRenderingContext,
01381                             const nsRect&        aDirtyRect,
01382                             nsFramePaintLayer    aWhichLayer,
01383                             PRUint32             aFlags)
01384 {
01385   PRBool clip = GetStyleDisplay()->IsTableClip();
01386   // If overflow is hidden then set the clip rect so that children don't
01387   // leak out of us. Note that because overflow'-clip' only applies to
01388   // the content area we do this after painting the border and background
01389   if (clip) {
01390     aRenderingContext.PushState();
01391     SetOverflowClipRect(aRenderingContext);
01392   }
01393 
01394   nsHTMLContainerFrame::PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer, aFlags);
01395 
01396   if (clip)
01397     aRenderingContext.PopState();
01398 }
01399 
01400 // table paint code is concerned primarily with borders and bg color
01401 // SEC: TODO: adjust the rect for captions 
01402 NS_METHOD 
01403 nsTableFrame::Paint(nsPresContext*      aPresContext,
01404                     nsIRenderingContext& aRenderingContext,
01405                     const nsRect&        aDirtyRect,
01406                     nsFramePaintLayer    aWhichLayer,
01407                     PRUint32             aFlags)
01408 {
01409   if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
01410     TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table,
01411                                    aPresContext, aRenderingContext, aDirtyRect);
01412     nsresult rv;
01413 
01414     if (eCompatibility_NavQuirks == aPresContext->CompatibilityMode()) {
01415       nsMargin deflate(0,0,0,0);
01416       if (IsBorderCollapse()) {
01417         GET_PIXELS_TO_TWIPS(aPresContext, p2t);
01418         BCPropertyData* propData =
01419           (BCPropertyData*)nsTableFrame::GetProperty((nsIFrame*)this,
01420                                                      nsLayoutAtoms::tableBCProperty,
01421                                                      PR_FALSE);
01422         if (propData) {
01423           deflate.top    = BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
01424           deflate.right  = BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
01425           deflate.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
01426           deflate.left   = BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
01427         }
01428       }
01429       rv = painter.PaintTable(this, &deflate);
01430       if (NS_FAILED(rv)) return rv;
01431     }
01432     else {
01433       rv = painter.PaintTable(this, nsnull);
01434       if (NS_FAILED(rv)) return rv;
01435     }
01436 
01437     if (GetStyleVisibility()->IsVisible()) {
01438       const nsStyleBorder* border = GetStyleBorder();
01439       nsRect  rect(0, 0, mRect.width, mRect.height);
01440       if (!IsBorderCollapse()) {
01441         PRIntn skipSides = GetSkipSides();
01442         nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
01443                                     aDirtyRect, rect, *border, mStyleContext,
01444                                     skipSides);
01445       }
01446       else {
01447         PaintBCBorders(aRenderingContext, aDirtyRect);
01448       }
01449     }
01450     aFlags |= NS_PAINT_FLAG_TABLE_BG_PAINT;
01451     aFlags &= ~NS_PAINT_FLAG_TABLE_CELL_BG_PASS;
01452   }
01453 
01454   PaintChildren(aPresContext, aRenderingContext, aDirtyRect,
01455                 aWhichLayer, aFlags);
01456 
01457   // Paint outline
01458   nsRect rect(0, 0, mRect.width, mRect.height);
01459   const nsStyleOutline* outlineStyle = GetStyleOutline();
01460   const nsStyleBorder* borderStyle  = GetStyleBorder();
01461   nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
01462                                aDirtyRect, rect, *borderStyle, *outlineStyle,
01463                                mStyleContext, 0);
01464 #ifdef DEBUG
01465   // for debug...
01466   if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) && GetShowFrameBorders()) {
01467     aRenderingContext.SetColor(NS_RGB(0,255,0));
01468     aRenderingContext.DrawRect(0, 0, mRect.width, mRect.height);
01469   }
01470 #endif
01471 
01472   DO_GLOBAL_REFLOW_COUNT_DSP_J("nsTableFrame", &aRenderingContext, NS_RGB(255,128,255));
01473   return NS_OK;
01474   /*nsFrame::Paint(aPresContext,
01475                         aRenderingContext,
01476                         aDirtyRect,
01477                         aWhichLayer);*/
01478 }
01479 
01480 NS_IMETHODIMP
01481 nsTableFrame::GetFrameForPoint(const nsPoint& aPoint, 
01482                                    nsFramePaintLayer aWhichLayer,
01483                                    nsIFrame**     aFrame)
01484 {
01485   // this should act like a block, so we need to override
01486   return GetFrameForPointUsing(aPoint, nsnull, aWhichLayer, (aWhichLayer == NS_FRAME_PAINT_LAYER_BACKGROUND), aFrame);
01487 }
01488 
01489 
01490 //null range means the whole thing
01491 NS_IMETHODIMP
01492 nsTableFrame::SetSelected(nsPresContext* aPresContext,
01493                           nsIDOMRange *aRange,
01494                           PRBool aSelected,
01495                           nsSpread aSpread)
01496 {
01497 #if 0
01498   //traverse through children unselect tables
01499   if ((aSpread == eSpreadDown)){
01500     nsIFrame* kid = GetFirstChild(nsnull);
01501     while (kid) {
01502       kid->SetSelected(nsnull, aSelected, eSpreadDown);
01503       kid = kid->GetNextSibling();
01504     }
01505   }
01506 #endif
01507   // Must call base class to set mSelected state and trigger repaint of frame
01508   // Note that in current version, aRange and aSpread are ignored,
01509   //   only this frame is considered
01510   nsFrame::SetSelected(aPresContext, aRange, aSelected, aSpread);
01511   return NS_OK;//return nsFrame::SetSelected(aRange,aSelected,eSpreadNone);
01512   
01513 }
01514 
01515 PRBool nsTableFrame::ParentDisablesSelection() const //override default behavior
01516 {
01517   PRBool returnval;
01518   if (NS_FAILED(GetSelected(&returnval)))
01519     return PR_FALSE;
01520   if (returnval)
01521     return PR_TRUE;
01522   return nsFrame::ParentDisablesSelection();
01523 }
01524 
01525 PRIntn
01526 nsTableFrame::GetSkipSides() const
01527 {
01528   PRIntn skip = 0;
01529   // frame attribute was accounted for in nsHTMLTableElement::MapTableBorderInto
01530   // account for pagination
01531   if (nsnull != mPrevInFlow) {
01532     skip |= 1 << NS_SIDE_TOP;
01533   }
01534   if (nsnull != mNextInFlow) {
01535     skip |= 1 << NS_SIDE_BOTTOM;
01536   }
01537   return skip;
01538 }
01539 
01540 PRBool nsTableFrame::NeedsReflow(const nsHTMLReflowState& aReflowState)
01541 {
01542   PRBool result = PR_TRUE;
01543   if (eReflowReason_Resize == aReflowState.reason) {
01544     if (aReflowState.mFlags.mSpecialHeightReflow &&
01545         !NeedSpecialReflow()                   &&
01546         !NeedToInitiateSpecialReflow()) {
01547       result = PR_FALSE;
01548     }
01549   }
01550   else if ((eReflowReason_Incremental == aReflowState.reason) &&
01551            (NS_UNCONSTRAINEDSIZE == aReflowState.availableHeight)) {
01552     // It's an incremental reflow and we're in galley mode. Only
01553     // do a full reflow if we need to.
01554     result = NeedStrategyInit() || NeedStrategyBalance();
01555   }
01556   return result;
01557 }
01558 
01559 // Called by IR_TargetIsChild() after an incremental reflow of
01560 // aKidFrame. Only called if we don't need a full reflow, e.g., the
01561 // column widths haven't changed. Not used for paginated mode, so
01562 // we don't need to worry about split row group frames
01563 //
01564 // Slides all the row groups following aKidFrame by the specified
01565 // amount
01566 nsresult 
01567 nsTableFrame::AdjustSiblingsAfterReflow(nsTableReflowState& aReflowState,
01568                                         nsIFrame*           aKidFrame,
01569                                         nscoord             aDeltaY)
01570 {
01571   NS_PRECONDITION(NS_UNCONSTRAINEDSIZE == aReflowState.reflowState.availableHeight,
01572                   "we're not in galley mode");
01573 
01574   nscoord yInvalid = NS_UNCONSTRAINEDSIZE;
01575 
01576   // Get the ordered children and find aKidFrame in the list
01577   nsAutoVoidArray rowGroups;
01578   PRUint32 numRowGroups;
01579   OrderRowGroups(rowGroups, numRowGroups, nsnull);
01580   PRUint32 changeIndex;
01581   for (changeIndex = 0; changeIndex < numRowGroups; changeIndex++) {
01582     if (aKidFrame == rowGroups.ElementAt(changeIndex)) {
01583       break;
01584     }
01585   }
01586   changeIndex++; // set it to the next sibling
01587 
01588   for (PRUint32 rgX = changeIndex; rgX < numRowGroups; rgX++) {
01589     nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
01590     // Move the frames that follow aKidFrame by aDeltaY, and update the running
01591     // y-offset
01592     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame);
01593     if (!rgFrame) continue; // skip foreign frames
01594 
01595     // Get the frame's bounding rect
01596     nsRect kidRect = kidFrame->GetRect();
01597     yInvalid = PR_MIN(yInvalid, kidRect.y);
01598   
01599     // Adjust the running y-offset
01600     aReflowState.y += kidRect.height;
01601  
01602     // Adjust the y-origin if its position actually changed
01603     if (aDeltaY != 0) {
01604       kidRect.y += aDeltaY;
01605       kidFrame->SetPosition(nsPoint(kidRect.x, kidRect.y));
01606       RePositionViews(kidFrame);
01607     }
01608   }
01609   
01610   // Invalidate the area we offset.
01611   if (NS_UNCONSTRAINEDSIZE != yInvalid) {
01612     nsRect  dirtyRect(0, yInvalid, mRect.width, mRect.height - yInvalid);
01613     // XXX what if some of the cells have outlines?
01614     Invalidate(dirtyRect);
01615   }
01616 
01617   return NS_OK;
01618 }
01619 
01620 void
01621 nsTableFrame::SetColumnDimensions(nscoord         aHeight,
01622                                   const nsMargin& aBorderPadding)
01623 {
01624   nscoord cellSpacingX = GetCellSpacingX();
01625   nscoord cellSpacingY = GetCellSpacingY();
01626   nscoord colHeight = aHeight -= aBorderPadding.top + aBorderPadding.bottom +
01627                                  2* cellSpacingY;
01628 
01629   nsIFrame* colGroupFrame = mColGroups.FirstChild();
01630   PRInt32 colX = 0;
01631   nsPoint colGroupOrigin(aBorderPadding.left + cellSpacingX,
01632                          aBorderPadding.top + cellSpacingY);
01633   while (nsnull != colGroupFrame) {
01634     nscoord colGroupWidth = 0;
01635     nsIFrame* colFrame = colGroupFrame->GetFirstChild(nsnull);
01636     nsPoint colOrigin(0,0);
01637     while (nsnull != colFrame) {
01638       if (NS_STYLE_DISPLAY_TABLE_COLUMN ==
01639           colFrame->GetStyleDisplay()->mDisplay) {
01640         NS_ASSERTION(colX < GetColCount(), "invalid number of columns");
01641         nscoord colWidth = GetColumnWidth(colX);
01642         nsRect colRect(colOrigin.x, colOrigin.y, colWidth, colHeight);
01643         colFrame->SetRect(colRect);
01644         colOrigin.x += colWidth + cellSpacingX;
01645         colGroupWidth += colWidth + cellSpacingX;
01646         colX++;
01647       }
01648       colFrame = colFrame->GetNextSibling();
01649     }
01650     if (colGroupWidth) {
01651       colGroupWidth -= cellSpacingX;
01652     }
01653 
01654     nsRect colGroupRect(colGroupOrigin.x, colGroupOrigin.y, colGroupWidth, colHeight);
01655     colGroupFrame->SetRect(colGroupRect);
01656     colGroupFrame = colGroupFrame->GetNextSibling();
01657     colGroupOrigin.x += colGroupWidth + cellSpacingX;
01658   }
01659 }
01660 
01661 // SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
01662 
01663 // XXX this could be made more general to handle row modifications that change the
01664 // table height, but first we need to scrutinize every Invalidate
01665 static void
01666 ProcessRowInserted(nsTableFrame&   aTableFrame,
01667                    PRBool          aInvalidate,
01668                    nscoord         aNewHeight)
01669 {
01670   aTableFrame.SetRowInserted(PR_FALSE); // reset the bit that got us here
01671   nsAutoVoidArray rowGroups;
01672   PRUint32 numRowGroups;
01673   aTableFrame.OrderRowGroups(rowGroups, numRowGroups);
01674   // find the row group containing the inserted row
01675   for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
01676     nsTableRowGroupFrame* rgFrame = (nsTableRowGroupFrame*)rowGroups.ElementAt(rgX);
01677     if (!rgFrame) continue; // should never happen
01678     nsIFrame* childFrame = rgFrame->GetFirstChild(nsnull);
01679     // find the row that was inserted first
01680     while (childFrame) {
01681       if (nsLayoutAtoms::tableRowFrame == childFrame->GetType()) {
01682         nsTableRowFrame* rowFrame = (nsTableRowFrame*)childFrame;
01683         if (rowFrame->IsFirstInserted()) {
01684           rowFrame->SetFirstInserted(PR_FALSE);
01685           if (aInvalidate) {
01686             // damage the table from the 1st row inserted to the end of the table
01687             nscoord damageY = rgFrame->GetPosition().y + rowFrame->GetPosition().y;
01688             nsRect damageRect(0, damageY,
01689                               aTableFrame.GetSize().width, aNewHeight - damageY);
01690 
01691             aTableFrame.Invalidate(damageRect);
01692             aTableFrame.SetRowInserted(PR_FALSE);
01693           }
01694           return; // found it, so leave
01695         }
01696       }
01697       childFrame = childFrame->GetNextSibling();
01698     }
01699   }
01700 }
01701 
01702 // Return true if aStylePosition has a pct height
01703 static PRBool 
01704 IsPctStyleHeight(const nsStylePosition* aStylePosition)
01705 {
01706   return (aStylePosition && 
01707           (eStyleUnit_Percent == aStylePosition->mHeight.GetUnit()));
01708 }
01709 
01710 // Return true if aStylePosition has a coord height
01711 static PRBool 
01712 IsFixedStyleHeight(const nsStylePosition* aStylePosition)
01713 {
01714   return (aStylePosition && 
01715           (eStyleUnit_Coord == aStylePosition->mHeight.GetUnit()));
01716 }
01717 
01718 // Return true if any of aReflowState.frame's ancestors within the containing table
01719 // have a pct or fixed height
01720 static PRBool
01721 AncestorsHaveStyleHeight(const nsHTMLReflowState& aReflowState)
01722 {
01723   for (const nsHTMLReflowState* parentRS = aReflowState.parentReflowState;
01724        parentRS && parentRS->frame; 
01725        parentRS = parentRS->parentReflowState) {
01726     nsIAtom* frameType = parentRS->frame->GetType();
01727     if (IS_TABLE_CELL(frameType)                         ||
01728         (nsLayoutAtoms::tableRowFrame      == frameType) ||
01729         (nsLayoutAtoms::tableRowGroupFrame == frameType)) {
01730       if (::IsPctStyleHeight(parentRS->mStylePosition) || ::IsFixedStyleHeight(parentRS->mStylePosition)) {
01731         return PR_TRUE;
01732       }
01733     }
01734     else if (nsLayoutAtoms::tableFrame == frameType) {
01735       // we reached the containing table, so always return
01736       if (::IsPctStyleHeight(parentRS->mStylePosition) || ::IsFixedStyleHeight(parentRS->mStylePosition)) {
01737         return PR_TRUE;
01738       }
01739       else return PR_FALSE;
01740     }
01741   }
01742   return PR_FALSE;
01743 }
01744 
01745 // See if a special height reflow needs to occur and if so, call RequestSpecialHeightReflow
01746 void
01747 nsTableFrame::CheckRequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
01748 {
01749   if (!aReflowState.frame) ABORT0();
01750   nsIFrame* prevInFlow = aReflowState.frame->GetPrevInFlow();
01751 
01752   if (!prevInFlow                                             &&   // 1st in flow                                            && // 1st in flow
01753       ((NS_UNCONSTRAINEDSIZE == aReflowState.mComputedHeight) ||   // no computed height
01754        (0                    == aReflowState.mComputedHeight))  && 
01755       ::IsPctStyleHeight(aReflowState.mStylePosition)) {           // pct height
01756 
01757     if (::AncestorsHaveStyleHeight(aReflowState)) {
01758       nsTableFrame::RequestSpecialHeightReflow(aReflowState);
01759     }
01760   }
01761 }
01762 
01763 // Notify the frame and its ancestors (up to the containing table) that a special
01764 // height reflow will occur. During a special height reflow, a table, row group,
01765 // row, or cell returns the last size it was reflowed at. However, the table may 
01766 // change the height of row groups, rows, cells in DistributeHeightToRows after. 
01767 // And the row group can change the height of rows, cells in CalculateRowHeights.
01768 void
01769 nsTableFrame::RequestSpecialHeightReflow(const nsHTMLReflowState& aReflowState)
01770 {
01771   // notify the frame and its ancestors of the special reflow, stopping at the containing table
01772   for (const nsHTMLReflowState* rs = &aReflowState; rs && rs->frame; rs = rs->parentReflowState) {
01773     nsIAtom* frameType = rs->frame->GetType();
01774     if (IS_TABLE_CELL(frameType)) {
01775       ((nsTableCellFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE);
01776     }
01777     else if (nsLayoutAtoms::tableRowFrame == frameType) {
01778       ((nsTableRowFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE);
01779     }
01780     else if (nsLayoutAtoms::tableRowGroupFrame == frameType) {
01781       ((nsTableRowGroupFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE);
01782     }
01783     else if (nsLayoutAtoms::tableFrame == frameType) {
01784       if (rs == &aReflowState) {
01785         // don't stop because we started with this table 
01786         ((nsTableFrame*)rs->frame)->SetNeedSpecialReflow(PR_TRUE);
01787       }
01788       else {
01789         ((nsTableFrame*)rs->frame)->SetNeedToInitiateSpecialReflow(PR_TRUE);
01790         // always stop when we reach a table that we didn't start with
01791         break;
01792       }
01793     }
01794   }
01795 }
01796 
01797 // Return true (and set aMetrics's desiredSize to aRect) if the special height reflow
01798 // was initiated by an ancestor of aReflowState.frame's containing table. In that case, 
01799 // aFrame's containing table will eventually initiate a special height reflow which 
01800 // will cause this method to return false. 
01801 PRBool
01802 nsTableFrame::IsPrematureSpecialHeightReflow(const nsHTMLReflowState& aReflowState,
01803                                              const nsRect&            aRect,
01804                                              PRBool                   aNeedSpecialHeightReflow,
01805                                              nsHTMLReflowMetrics&     aMetrics)
01806 {
01807   PRBool premature = PR_FALSE; 
01808   if (aReflowState.mFlags.mSpecialHeightReflow) { 
01809     if (aNeedSpecialHeightReflow) { 
01810       nsTableFrame* tableFrame; 
01811       nsTableFrame::GetTableFrame(aReflowState.frame, tableFrame); 
01812       if (tableFrame && (tableFrame != aReflowState.mPercentHeightReflowInitiator)) { 
01813         premature = PR_TRUE; 
01814       } 
01815     } 
01816     else { 
01817       premature = PR_TRUE; 
01818     } 
01819     if (premature) { 
01820       aMetrics.width  = aRect.width; 
01821       aMetrics.height = aRect.height; 
01822     } 
01823   }
01824   return premature;
01825 }
01826 
01827 /******************************************************************************************
01828  * During the initial reflow the table reflows each child with an unconstrained avail width
01829  * to get its max element width and maximum width. This is referred to as the pass 1 reflow.
01830  *
01831  * After the 1st pass reflow, the table determines the column widths using BalanceColumnWidths()
01832  * then reflows each child again with a constrained avail width. This reflow is referred to
01833  * as the pass 2 reflow. 
01834  *
01835  * A special height reflow (pass 3 reflow) can occur during an intitial or resize reflow
01836  * if (a) a row group, row, cell, or a frame inside a cell has a percent height but no computed 
01837  * height or (b) in paginated mode, a table has a height. (a) supports percent nested tables 
01838  * contained inside cells whose heights aren't known until after the pass 2 reflow. (b) is 
01839  * necessary because the table cannot split until after the pass 2 reflow. The mechanics of 
01840  * the special height reflow (variety a) are as follows: 
01841  * 
01842  * 1) Each table related frame (table, row group, row, cell) implements NeedsSpecialReflow()
01843  *    to indicate that it should get the reflow. It does this when it has a percent height but 
01844  *    no computed height by calling CheckRequestSpecialHeightReflow(). This method calls
01845  *    RequestSpecialHeightReflow() which calls SetNeedSpecialReflow() on its ancestors until 
01846  *    it reaches the containing table and calls SetNeedToInitiateSpecialReflow() on it. For 
01847  *    percent height frames inside cells, during DidReflow(), the cell's NotifyPercentHeight()
01848  *    is called (the cell is the reflow state's mPercentHeightObserver in this case). 
01849  *    NotifyPercentHeight() calls RequestSpecialHeightReflow().
01850  *
01851  * 2) After the pass 2 reflow, if the table's NeedToInitiateSpecialReflow(true) was called, it
01852  *    will do the special height reflow, setting the reflow state's mFlages.mSpecialHeightReflow
01853  *    to true and mSpecialHeightInitiator to itself. It won't do this if IsPrematureSpecialHeightReflow()
01854  *    returns true because in that case another special height reflow will be comming along with the
01855  *    containing table as the mSpecialHeightInitiator. It is only relevant to do the reflow when
01856  *    the mSpecialHeightInitiator is the containing table, because if it is a remote ancestor, then
01857  *    appropriate heights will not be known.
01858  *
01859  * 3) Since the heights of the table, row groups, rows, and cells was determined during the pass 2
01860  *    reflow, they return their last desired sizes during the special height reflow. The reflow only
01861  *    permits percent height frames inside the cells to resize based on the cells height and that height
01862  *    was determined during the pass 2 reflow.
01863  *
01864  * So, in the case of deeply nested tables, all of the tables that were told to initiate a special
01865  * reflow will do so, but if a table is already in a special reflow, it won't inititate the reflow
01866  * until the current initiator is its containing table. Since these reflows are only received by
01867  * frames that need them and they don't cause any rebalancing of tables, the extra overhead is minimal.
01868  *
01869  * The type of special reflow that occurs during printing (variety b) follows the same mechanism except
01870  * that all frames will receive the reflow even if they don't really need them.
01871  *
01872  * Open issues with the special height reflow:
01873  *
01874  * 1) At some point there should be 2 kinds of special height reflows because (a) and (b) above are 
01875  *    really quite different. This would avoid unnecessary reflows during printing. 
01876  * 2) When a cell contains frames whose percent heights > 100%, there is data loss (see bug 115245). 
01877  *    However, this can also occur if a cell has a fixed height and there is no special height reflow. 
01878  *
01879  ******************************************************************************************/
01880 
01881 /* Layout the entire inner table. */
01882 NS_METHOD nsTableFrame::Reflow(nsPresContext*          aPresContext,
01883                                nsHTMLReflowMetrics&     aDesiredSize,
01884                                const nsHTMLReflowState& aReflowState,
01885                                nsReflowStatus&          aStatus)
01886 {
01887   DO_GLOBAL_REFLOW_COUNT("nsTableFrame", aReflowState.reason);
01888   DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
01889 #if defined DEBUG_TABLE_REFLOW_TIMING
01890   nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState);
01891 #endif
01892   PRBool isPaginated = aPresContext->IsPaginated();
01893 
01894   // If this is a special height reflow, set our desired size to what is was previously and return
01895   // if we will be getting another special height reflow. In paginated mode, SetNeedSpecialReflow(PR_TRUE) 
01896   // may not have been called if reflow was a result of having a height on the containing table
01897   if (IsPrematureSpecialHeightReflow(aReflowState, mRect, NeedSpecialReflow() || isPaginated, aDesiredSize)) 
01898     return NS_OK;
01899 
01900   aStatus = NS_FRAME_COMPLETE; 
01901   if (!mPrevInFlow && !mTableLayoutStrategy) {
01902     NS_ASSERTION(PR_FALSE, "strategy should have been created in Init");
01903     return NS_ERROR_NULL_POINTER;
01904   }
01905   nsresult rv = NS_OK;
01906 
01907   // see if a special height reflow needs to occur due to having a pct height
01908   if (!NeedSpecialReflow()) 
01909     nsTableFrame::CheckRequestSpecialHeightReflow(aReflowState);
01910 
01911   // see if collapsing borders need to be calculated
01912   if (!mPrevInFlow && IsBorderCollapse() && NeedToCalcBCBorders()) {
01913     GET_TWIPS_TO_PIXELS(aPresContext, p2t);
01914     CalcBCBorders();
01915   }
01916 
01917   aDesiredSize.width = aReflowState.availableWidth;
01918 
01919   nsReflowReason nextReason = aReflowState.reason;
01920 
01921   // Check for an overflow list, and append any row group frames being pushed
01922   MoveOverflowToChildList(aPresContext);
01923 
01924   // Processes an initial (except when there is mPrevInFlow), incremental, or style 
01925   // change reflow 1st. resize reflows are processed in the next phase.
01926   switch (aReflowState.reason) {
01927     case eReflowReason_Initial: 
01928     case eReflowReason_StyleChange: {
01929       if ((eReflowReason_Initial == aReflowState.reason) && HadInitialReflow()) {
01930               // XXX this could be an assertion and the if removed
01931         NS_NOTREACHED("intial reflow called twice");
01932       }
01933       else {
01934         if (!mPrevInFlow) { // only do pass1 on a first in flow
01935           if (IsAutoLayout()) {     
01936             // only do pass1 reflow on an auto layout table
01937             nsTableReflowState reflowState(*aPresContext, aReflowState, *this,
01938                                            aReflowState.reason,
01939                                            NS_UNCONSTRAINEDSIZE,
01940                                            NS_UNCONSTRAINEDSIZE);
01941             // reflow the children
01942             nsIFrame *lastReflowed;
01943             nsRect overflowArea;
01944             ReflowChildren(reflowState, !HaveReflowedColGroups(),
01945                            PR_FALSE, aStatus, lastReflowed,
01946                            overflowArea);
01947           }
01948           mTableLayoutStrategy->Initialize(aReflowState);
01949         }
01950       }
01951       SetHadInitialReflow(PR_TRUE);
01952       if (!mPrevInFlow) {
01953         SetNeedStrategyBalance(PR_TRUE); // force a balance and then a pass2 reflow 
01954         if ((nextReason != eReflowReason_StyleChange) || IsAutoLayout()) 
01955           nextReason = eReflowReason_Resize;
01956       }
01957       else {
01958         nextReason = eReflowReason_Initial;
01959       }
01960       break; 
01961     }
01962     case eReflowReason_Incremental:
01963       NS_ASSERTION(HadInitialReflow(), "intial reflow not called");
01964       rv = IncrementalReflow(aReflowState, aStatus);
01965       nextReason = eReflowReason_Resize;
01966       break;
01967     case eReflowReason_Resize:
01968       // do the resize reflow below
01969       if (!HadInitialReflow()) {
01970         NS_ASSERTION(HadInitialReflow(), "intial reflow not called");
01971         nextReason = eReflowReason_Initial;
01972       }
01973       NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth,
01974                    "this doesn't do anything");
01975       SetNeedStrategyBalance(PR_TRUE); 
01976       break; 
01977     default:
01978       break;
01979   }
01980 
01981   if (NS_FAILED(rv)) return rv;
01982 
01983   PRBool haveDesiredHeight = PR_FALSE;
01984   PRBool balanced          = PR_FALSE;
01985   PRBool reflowedChildren  = PR_FALSE;
01986 
01987   // Reflow the entire table (pass 2 and possibly pass 3). This phase is necessary during a 
01988   // constrained initial reflow and other reflows which require either a strategy init or balance. 
01989   // This isn't done during an unconstrained reflow, because it will occur later when the parent 
01990   // reflows with a constrained width.
01991   if (NeedsReflow(aReflowState) && (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth)) {
01992     // see if an extra reflow will be necessary in pagination mode when there is a specified table height 
01993     if (isPaginated && !mPrevInFlow && (NS_UNCONSTRAINEDSIZE != aReflowState.availableHeight)) {
01994       nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
01995       if ((tableSpecifiedHeight > 0) && 
01996           (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE)) {
01997         SetNeedToInitiateSpecialReflow(PR_TRUE);
01998       }
01999     }
02000     nsIFrame* lastChildReflowed = nsnull;
02001     PRBool willInitiateSpecialReflow = 
02002       ((NeedToInitiateSpecialReflow() || InitiatedSpecialReflow()) && 
02003        (aReflowState.mFlags.mSpecialHeightReflow || !NeedSpecialReflow()));
02004 
02005     // do the pass 2 reflow unless this is a special height reflow and we will be 
02006     // initiating a special height reflow
02007     if (!(aReflowState.mFlags.mSpecialHeightReflow && willInitiateSpecialReflow)) {
02008       // if we need to initiate a special height reflow, then don't constrain the 
02009       // height of the reflow before that
02010       nscoord availHeight = (willInitiateSpecialReflow)
02011                             ? NS_UNCONSTRAINEDSIZE : aReflowState.availableHeight;
02012 
02013       ReflowTable(aDesiredSize, aReflowState, availHeight, nextReason, 
02014                   lastChildReflowed, balanced, aStatus);
02015       nextReason = eReflowReason_Resize;
02016       reflowedChildren = PR_TRUE;
02017     }
02018     // reevaluate special height reflow conditions
02019     if ((NeedToInitiateSpecialReflow() || InitiatedSpecialReflow()) &&
02020         (aReflowState.mFlags.mSpecialHeightReflow || !NeedSpecialReflow()) &&
02021         NS_FRAME_IS_COMPLETE(aStatus)) {
02022       // distribute extra vertical space to rows
02023       CalcDesiredHeight(aReflowState, aDesiredSize); 
02024       ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = PR_TRUE;
02025       // save the previous special height reflow initiator, install us as the new one
02026       nsIFrame* specialReflowInitiator = aReflowState.mPercentHeightReflowInitiator;
02027       ((nsHTMLReflowState&)aReflowState).mPercentHeightReflowInitiator = this;
02028 
02029       ((nsHTMLReflowState::ReflowStateFlags&)aReflowState.mFlags).mSpecialHeightReflow = PR_TRUE;
02030       ReflowTable(aDesiredSize, aReflowState, aReflowState.availableHeight, 
02031                   nextReason, lastChildReflowed, balanced, aStatus);
02032       // restore the previous special height reflow initiator
02033       ((nsHTMLReflowState&)aReflowState).mPercentHeightReflowInitiator = specialReflowInitiator;
02034       // XXX We should call SetInitiatedSpecialReflow(PR_FALSE) at some point, but it is difficult to tell when
02035       SetInitiatedSpecialReflow(PR_TRUE);
02036 
02037       if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
02038         // if there is an incomplete child, then set the desired height to include it but not the next one
02039         nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
02040         aDesiredSize.height = borderPadding.bottom + GetCellSpacingY() +
02041                               lastChildReflowed->GetRect().YMost();
02042       }
02043       haveDesiredHeight = PR_TRUE;
02044       reflowedChildren  = PR_TRUE;
02045     }
02046   }
02047   else if (aReflowState.mFlags.mSpecialHeightReflow) {
02048     aDesiredSize.width  = mRect.width;
02049     aDesiredSize.height = mRect.height;
02050 #if defined DEBUG_TABLE_REFLOW_TIMING
02051     nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus);
02052 #endif
02053     SetNeedSpecialReflow(PR_FALSE);
02054     SetNeedToInitiateSpecialReflow(PR_FALSE);
02055     return NS_OK;
02056   }
02057 
02058   aDesiredSize.width = GetDesiredWidth();
02059   if (!haveDesiredHeight) {
02060     CalcDesiredHeight(aReflowState, aDesiredSize); 
02061   }
02062   if (IsRowInserted()) {
02063     ProcessRowInserted(*this, PR_TRUE, aDesiredSize.height);
02064   }
02065 
02066   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
02067   SetColumnDimensions(aDesiredSize.height, borderPadding);
02068   if (NeedToCollapseRows()) {
02069     AdjustForCollapsingRows(aDesiredSize);
02070   }
02071   if (NeedToCollapseColumns()) {
02072     AdjustForCollapsingCols(aDesiredSize);
02073   }
02074 
02075   // See if we need to calc max elem and/or preferred widths. This isn't done on 
02076   // continuations or if we have balanced (since it was done then) 
02077   if ((aDesiredSize.mComputeMEW || (aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH)) &&
02078       !mPrevInFlow && !balanced) {
02079     // Since the calculation has some cost, avoid doing it for an unconstrained initial 
02080     // reflow (it was done when the strategy was initialized in pass 1 above) and most
02081     // unconstrained resize reflows. XXX The latter optimization could be a problem if the
02082     // parent of a nested table starts doing unconstrained resize reflows to get max elem/preferred 
02083     if ((NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) ||
02084         (eReflowReason_Incremental == aReflowState.reason)    || 
02085         (eReflowReason_StyleChange == aReflowState.reason)    ||
02086         ((eReflowReason_Resize == aReflowState.reason) &&
02087          HasPctCol() && IsAutoWidth())) {
02088       nscoord minWidth, prefWidth;
02089       CalcMinAndPreferredWidths(aReflowState, PR_TRUE, minWidth, prefWidth);
02090       SetMinWidth(minWidth);
02091       SetPreferredWidth(prefWidth);
02092     }
02093   }
02094   // See if we need to return our max element size
02095   if (aDesiredSize.mComputeMEW) {
02096     aDesiredSize.mMaxElementWidth  = GetMinWidth();
02097   }
02098   // See if we need to return our maximum width
02099   if (aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH) {
02100     aDesiredSize.mMaximumWidth = GetPreferredWidth();
02101   }
02102   // make sure the table overflow area does include the table rect.
02103   nsRect tableRect(0, 0, aDesiredSize.width, aDesiredSize.height) ;
02104   
02105   if (!aReflowState.mStyleDisplay->IsTableClip()) {
02106     // collapsed border may leak out
02107     nsMargin bcMargin = GetBCMargin();
02108     tableRect.Inflate(bcMargin);
02109   }
02110   aDesiredSize.mOverflowArea.UnionRect(aDesiredSize.mOverflowArea, tableRect);
02111   
02112   if (aReflowState.mFlags.mSpecialHeightReflow) {
02113     SetNeedSpecialReflow(PR_FALSE);
02114     SetNeedToInitiateSpecialReflow(PR_FALSE);
02115   }
02116 #if defined DEBUG_TABLE_REFLOW_TIMING
02117   nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus);
02118 #endif
02119 
02120   // If we reflowed all the rows, then invalidate the largest possible area that either the
02121   // table occupied before this reflow or will occupy after.
02122   if (reflowedChildren) {
02123     nsRect damage(0, 0, PR_MAX(mRect.width, aDesiredSize.width),
02124                   PR_MAX(mRect.height, aDesiredSize.height));
02125     damage.UnionRect(damage, aDesiredSize.mOverflowArea);
02126     nsRect* oldOverflowArea = GetOverflowAreaProperty();
02127     if (oldOverflowArea) {
02128       damage.UnionRect(damage, *oldOverflowArea);
02129     }
02130     Invalidate(damage);
02131   } else {
02132     // use the old overflow area
02133      nsRect* oldOverflowArea = GetOverflowAreaProperty();
02134      if (oldOverflowArea) {
02135        aDesiredSize.mOverflowArea.UnionRect(aDesiredSize.mOverflowArea, *oldOverflowArea);
02136      }
02137   }
02138 
02139   FinishAndStoreOverflow(&aDesiredSize);
02140   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
02141   return rv;
02142 }
02143 
02144 nsresult 
02145 nsTableFrame::ReflowTable(nsHTMLReflowMetrics&     aDesiredSize,
02146                           const nsHTMLReflowState& aReflowState,
02147                           nscoord                  aAvailHeight,
02148                           nsReflowReason           aReason,
02149                           nsIFrame*&               aLastChildReflowed,
02150                           PRBool&                  aDidBalance,
02151                           nsReflowStatus&          aStatus)
02152 {
02153   nsresult rv = NS_OK;
02154   aDidBalance = PR_FALSE;
02155   aLastChildReflowed = nsnull;
02156 
02157   PRBool haveReflowedColGroups = PR_TRUE;
02158   if (!mPrevInFlow) {
02159     if (NeedStrategyInit()) {
02160       mTableLayoutStrategy->Initialize(aReflowState);
02161       BalanceColumnWidths(aReflowState); 
02162       aDidBalance = PR_TRUE;
02163     }
02164     if (NeedStrategyBalance()) {
02165       BalanceColumnWidths(aReflowState);
02166       aDidBalance = PR_TRUE;
02167     }
02168     haveReflowedColGroups = HaveReflowedColGroups();
02169   }
02170   // Constrain our reflow width to the computed table width (of the 1st in flow).
02171   // and our reflow height to our avail height minus border, padding, cellspacing
02172   aDesiredSize.width = GetDesiredWidth();
02173   nsTableReflowState reflowState(*GetPresContext(), aReflowState, *this, aReason, 
02174                                  aDesiredSize.width, aAvailHeight);
02175   ReflowChildren(reflowState, haveReflowedColGroups, PR_FALSE,
02176                  aStatus, aLastChildReflowed, aDesiredSize.mOverflowArea);
02177 
02178   if (eReflowReason_Resize == aReflowState.reason) {
02179     if (!DidResizeReflow()) {
02180       SetResizeReflow(PR_TRUE);
02181     }
02182   }  
02183   return rv;
02184 }
02185 
02186 nsIFrame*
02187 nsTableFrame::GetFirstBodyRowGroupFrame()
02188 {
02189   nsIFrame* headerFrame = nsnull;
02190   nsIFrame* footerFrame = nsnull;
02191 
02192   for (nsIFrame* kidFrame = mFrames.FirstChild(); nsnull != kidFrame; ) {
02193     const nsStyleDisplay* childDisplay = kidFrame->GetStyleDisplay();
02194 
02195     // We expect the header and footer row group frames to be first, and we only
02196     // allow one header and one footer
02197     if (NS_STYLE_DISPLAY_TABLE_HEADER_GROUP == childDisplay->mDisplay) {
02198       if (headerFrame) {
02199         // We already have a header frame and so this header frame is treated
02200         // like an ordinary body row group frame
02201         return kidFrame;
02202       }
02203       headerFrame = kidFrame;
02204     
02205     } else if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) {
02206       if (footerFrame) {
02207         // We already have a footer frame and so this footer frame is treated
02208         // like an ordinary body row group frame
02209         return kidFrame;
02210       }
02211       footerFrame = kidFrame;
02212 
02213     } else if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == childDisplay->mDisplay) {
02214       return kidFrame;
02215     }
02216 
02217     // Get the next child
02218     kidFrame = kidFrame->GetNextSibling();
02219   }
02220 
02221   return nsnull;
02222 }
02223 
02224 // Table specific version that takes into account repeated header and footer
02225 // frames when continuing table frames
02226 void
02227 nsTableFrame::PushChildren(const nsAutoVoidArray& aFrames,
02228                            PRInt32 aPushFrom)
02229 {
02230   NS_PRECONDITION(aPushFrom > 0, "pushing first child");
02231 
02232   // extract the frames from the array into a sibling list
02233   nsFrameList frames;
02234   nsIFrame* lastFrame = nsnull;
02235   PRUint32 childX;
02236   nsIFrame* prevSiblingHint =
02237     NS_STATIC_CAST(nsIFrame*, aFrames.ElementAt(aPushFrom - 1));
02238   for (childX = aPushFrom; childX < aFrames.Count(); ++childX) {
02239     nsIFrame* f = NS_STATIC_CAST(nsIFrame*, aFrames.FastElementAt(childX));
02240     // Don't push repeatable frames, do push non-rowgroup frames
02241     if (f->GetType() != nsLayoutAtoms::tableRowGroupFrame ||
02242         !NS_STATIC_CAST(nsTableRowGroupFrame*, f)->IsRepeatable()) {
02243       mFrames.RemoveFrame(f, prevSiblingHint);
02244       frames.InsertFrame(nsnull, lastFrame, f);
02245       lastFrame = f;
02246     }
02247   }
02248 
02249   if (nsnull != mNextInFlow) {
02250     nsTableFrame* nextInFlow = (nsTableFrame*)mNextInFlow;
02251 
02252     // Insert the frames after any repeated header and footer frames
02253     nsIFrame* firstBodyFrame = nextInFlow->GetFirstBodyRowGroupFrame();
02254     nsIFrame* prevSibling = nsnull;
02255     if (firstBodyFrame) {
02256       prevSibling = nextInFlow->mFrames.GetPrevSiblingFor(firstBodyFrame);
02257     }
02258     // When pushing and pulling frames we need to check for whether any
02259     // views need to be reparented.
02260     for (nsIFrame* f = frames.FirstChild(); f; f = f->GetNextSibling()) {
02261       nsHTMLContainerFrame::ReparentFrameView(GetPresContext(), f, this, nextInFlow);
02262     }
02263     nextInFlow->mFrames.InsertFrames(mNextInFlow, prevSibling, frames.FirstChild());
02264   }
02265   else {
02266     // Add the frames to our overflow list
02267     SetOverflowFrames(GetPresContext(), frames.FirstChild());
02268   }
02269 }
02270 
02271 // Table specific version that takes into account header and footer row group
02272 // frames that are repeated for continuing table frames
02273 //
02274 // Appends the overflow frames to the end of the child list, just like the
02275 // nsContainerFrame version does, except that there are no assertions that
02276 // the child list is empty (it may not be empty, because there may be repeated
02277 // header/footer frames)
02278 PRBool
02279 nsTableFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
02280 {
02281   PRBool result = PR_FALSE;
02282 
02283   // Check for an overflow list with our prev-in-flow
02284   nsTableFrame* prevInFlow = (nsTableFrame*)mPrevInFlow;
02285   if (prevInFlow) {
02286     nsIFrame* prevOverflowFrames = prevInFlow->GetOverflowFrames(aPresContext, PR_TRUE);
02287     if (prevOverflowFrames) {
02288       // When pushing and pulling frames we need to check for whether any
02289       // views need to be reparented.
02290       for (nsIFrame* f = prevOverflowFrames; f; f = f->GetNextSibling()) {
02291         nsHTMLContainerFrame::ReparentFrameView(aPresContext, f, prevInFlow, this);
02292       }
02293       mFrames.AppendFrames(this, prevOverflowFrames);
02294       result = PR_TRUE;
02295     }
02296   }
02297 
02298   // It's also possible that we have an overflow list for ourselves
02299   nsIFrame* overflowFrames = GetOverflowFrames(aPresContext, PR_TRUE);
02300   if (overflowFrames) {
02301     mFrames.AppendFrames(nsnull, overflowFrames);
02302     result = PR_TRUE;
02303   }
02304   return result;
02305 }
02306 
02307 NS_METHOD 
02308 nsTableFrame::CollapseRowGroupIfNecessary(nsIFrame* aRowGroupFrame,
02309                                           const nscoord& aYTotalOffset,
02310                                           nscoord& aYGroupOffset, PRInt32& aRowX)
02311 {
02312   const nsStyleVisibility* groupVis = aRowGroupFrame->GetStyleVisibility();
02313   
02314   PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
02315   if (collapseGroup) {
02316     SetNeedToCollapseRows(PR_TRUE);
02317   }
02318   nsIFrame* rowFrame = aRowGroupFrame->GetFirstChild(nsnull);
02319 
02320   while (nsnull != rowFrame) {
02321     const nsStyleDisplay* rowDisplay = rowFrame->GetStyleDisplay();
02322     if (NS_STYLE_DISPLAY_TABLE_ROW == rowDisplay->mDisplay) {
02323       const nsStyleVisibility* rowVis = rowFrame->GetStyleVisibility();
02324       PRBool collapseRow = (NS_STYLE_VISIBILITY_COLLAPSE == rowVis->mVisible);
02325       if (collapseRow) {
02326         SetNeedToCollapseRows(PR_TRUE);
02327       }
02328       nsRect rowRect = rowFrame->GetRect();
02329       if (collapseGroup || collapseRow) {
02330         aYGroupOffset += rowRect.height;
02331         rowRect.height = 0;
02332         rowFrame->SetRect(rowRect);
02333         nsIFrame* cellFrame = rowFrame->GetFirstChild(nsnull);
02334         while (nsnull != cellFrame) {
02335           const nsStyleDisplay* cellDisplay = cellFrame->GetStyleDisplay();
02336           if (NS_STYLE_DISPLAY_TABLE_CELL == cellDisplay->mDisplay) {
02337             nsTableCellFrame* cFrame = (nsTableCellFrame*)cellFrame;
02338             nsRect cRect = cFrame->GetRect();
02339             cRect.height -= rowRect.height;
02340             cFrame->SetCollapseOffsetY(-aYGroupOffset);
02341             cFrame->SetRect(cRect);
02342           }
02343           cellFrame = cellFrame->GetNextSibling();
02344         }
02345         // check if a cell above spans into here
02346         nsTableCellMap* cellMap = GetCellMap();
02347         if (cellMap) {
02348           PRInt32 numCols = cellMap->GetColCount();
02349           nsTableCellFrame* lastCell = nsnull;
02350           for (int colX = 0; colX < numCols; colX++) {
02351             CellData* cellData = cellMap->GetDataAt(aRowX, colX);
02352             if (cellData && cellData->IsRowSpan()) { // a cell above is spanning into here
02353               // adjust the real cell's rect only once
02354               nsTableCellFrame* realCell = cellMap->GetCellFrame(aRowX, colX, *cellData, PR_TRUE);
02355               NS_ASSERTION(realCell, "row span without origin?");
02356               if (realCell && (realCell != lastCell)) {
02357                 nsRect realRect = realCell->GetRect();
02358                 realRect.height -= rowRect.height;
02359                 realCell->SetRect(realRect);
02360               }
02361               lastCell = realCell;
02362             }
02363           }
02364         }
02365       } else { // row is not collapsed but needs to be adjusted by those that are
02366         rowRect.y -= aYGroupOffset;
02367         rowFrame->SetRect(rowRect);
02368         // reset the collapse yoffset
02369         nsIFrame* cellFrame = rowFrame->GetFirstChild(nsnull);
02370         while (cellFrame) {
02371           const nsStyleDisplay* cellDisplay = cellFrame->GetStyleDisplay();
02372           if (NS_STYLE_DISPLAY_TABLE_CELL == cellDisplay->mDisplay) {
02373             nsTableCellFrame* cFrame = (nsTableCellFrame*)cellFrame;
02374             // reset the offset as this row is not collapsed
02375             cFrame->SetCollapseOffsetY(0);
02376           }
02377           cellFrame = cellFrame->GetNextSibling();
02378         }
02379       }
02380       aRowX++;
02381     }
02382     rowFrame = rowFrame->GetNextSibling();
02383   } // end row frame while
02384 
02385   nsRect groupRect = aRowGroupFrame->GetRect();
02386   groupRect.height -= aYGroupOffset;
02387   groupRect.y -= aYTotalOffset;
02388   aRowGroupFrame->SetRect(groupRect);
02389 
02390   return NS_OK;
02391 }
02392 
02393 // collapsing row groups, rows, col groups and cols are accounted for after both passes of
02394 // reflow so that it has no effect on the calculations of reflow.
02395 NS_METHOD nsTableFrame::AdjustForCollapsingRows(nsHTMLReflowMetrics& aDesiredSize)
02396 {
02397   nscoord yGroupOffset = 0; // total offset among rows within a single row group
02398   nscoord yTotalOffset = 0; // total offset among all rows in all row groups
02399   PRInt32 rowIndex = 0;
02400   // reset the bit, it will be set again if row/rowgroup is collapsed
02401   SetNeedToCollapseRows(PR_FALSE); 
02402   
02403   // collapse the rows and/or row groups as necessary
02404   // Get the ordered children
02405   nsAutoVoidArray rowGroups;
02406   PRUint32 numRowGroups;
02407   OrderRowGroups(rowGroups, numRowGroups);
02408   
02409   // Walk the list of children
02410   for (PRUint32 childX = 0; childX < numRowGroups; childX++) {
02411     nsIFrame* childFrame = (nsIFrame*)rowGroups.ElementAt(childX);
02412     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(childFrame);
02413     if (!rgFrame) continue; // skip foreign frame types
02414     CollapseRowGroupIfNecessary(rgFrame, yTotalOffset, yGroupOffset, rowIndex);
02415     yTotalOffset += yGroupOffset;
02416     yGroupOffset = 0;
02417   } 
02418 
02419   aDesiredSize.height -= yTotalOffset;
02420  
02421   return NS_OK;
02422 }
02423 
02424 NS_METHOD nsTableFrame::AdjustForCollapsingCols(nsHTMLReflowMetrics& aDesiredSize)
02425 {
02426   nsTableCellMap* cellMap = GetCellMap();
02427   if (!cellMap) return NS_OK;
02428    // reset the bit, it will be set again if col/colgroup is collapsed
02429   SetNeedToCollapseColumns(PR_FALSE);
02430  
02431   PRInt32 numRows = cellMap->GetRowCount();
02432   nsTableIterator groupIter(mColGroups, eTableDIR);
02433   nsIFrame* groupFrame = groupIter.First(); 
02434   nscoord cellSpacingX = GetCellSpacingX();
02435   nscoord xOffset = 0;
02436   PRInt32 colX = (groupIter.IsLeftToRight()) ? 0 : GetColCount() - 1; 
02437   PRInt32 direction = (groupIter.IsLeftToRight()) ? 1 : -1; 
02438   // iterate over the col groups
02439   while (nsnull != groupFrame) {
02440     const nsStyleVisibility* groupVis = groupFrame->GetStyleVisibility();
02441     
02442     PRBool collapseGroup = (NS_STYLE_VISIBILITY_COLLAPSE == groupVis->mVisible);
02443     if (collapseGroup) {
02444       SetNeedToCollapseColumns(PR_TRUE);
02445     }
02446     nsTableIterator colIter(*groupFrame, eTableDIR);
02447     nsIFrame* colFrame = colIter.First();
02448     // iterate over the cols in the col group
02449     while (nsnull != colFrame) {
02450       const nsStyleDisplay* colDisplay = colFrame->GetStyleDisplay();
02451       if (NS_STYLE_DISPLAY_TABLE_COLUMN == colDisplay->mDisplay) {
02452         const nsStyleVisibility* colVis = colFrame->GetStyleVisibility();
02453         PRBool collapseCol = (NS_STYLE_VISIBILITY_COLLAPSE == colVis->mVisible);
02454         if (collapseCol) {
02455           SetNeedToCollapseColumns(PR_TRUE);
02456         }
02457         PRInt32 colWidth = GetColumnWidth(colX);
02458         if (collapseGroup || collapseCol) {
02459           xOffset += colWidth + cellSpacingX;
02460         }
02461         nsTableCellFrame* lastCell  = nsnull;
02462         nsTableCellFrame* cellFrame = nsnull;
02463         for (PRInt32 rowX = 0; rowX < numRows; rowX++) {
02464           CellData* cellData = cellMap->GetDataAt(rowX, colX);
02465           if (cellData) {
02466             if (cellData->IsOrig()) { // the cell originates at (rowX, colX)
02467               cellFrame = cellData->GetCellFrame();
02468               // reset the collapse offsets since they may have been collapsed previously
02469               cellFrame->SetCollapseOffsetX(0);
02470               cellFrame->SetCollapseOffsetY(0);
02471               nsRect cellRect = cellFrame->GetRect();
02472               if (collapseGroup || collapseCol) {
02473                 if (lastCell != cellFrame) { // do it only once if there is a row span
02474                   cellRect.width -= colWidth;
02475                   cellFrame->SetCollapseOffsetX(-xOffset);
02476                 }
02477               } else { // the cell is not in a collapsed col but needs to move
02478                 cellRect.x -= xOffset;
02479               }
02480               cellFrame->SetRect(cellRect);
02481               // if the cell does not originate at (rowX, colX), adjust the real cells width
02482             } else if (collapseGroup || collapseCol) { 
02483               if (cellData->IsColSpan()) {
02484                 cellFrame = cellMap->GetCellFrame(rowX, colX, *cellData, PR_FALSE);
02485               }
02486               if ((cellFrame) && (lastCell != cellFrame)) {
02487                 nsRect cellRect = cellFrame->GetRect();
02488                 cellRect.width -= colWidth + cellSpacingX;
02489                 cellFrame->SetRect(cellRect);
02490               }
02491             }
02492           }
02493           lastCell = cellFrame;
02494         }
02495         colX += direction;
02496       }
02497       colFrame = colIter.Next();
02498     } // inner while
02499     groupFrame = groupIter.Next();
02500   } // outer while
02501 
02502   aDesiredSize.width -= xOffset;
02503  
02504   return NS_OK;
02505 }
02506 
02507 NS_IMETHODIMP
02508 nsTableFrame::AppendFrames(nsIAtom*        aListName,
02509                            nsIFrame*       aFrameList)
02510 {
02511   // Because we actually have two child lists, one for col group frames and one
02512   // for everything else, we need to look at each frame individually
02513   nsIFrame* f = aFrameList;
02514   while (f) {
02515     // Get the next frame and disconnect this frame from its sibling
02516     nsIFrame* next = f->GetNextSibling();
02517     f->SetNextSibling(nsnull);
02518 
02519     // See what kind of frame we have
02520     const nsStyleDisplay* display = f->GetStyleDisplay();
02521 
02522     if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
02523       nsTableColGroupFrame* lastColGroup;
02524       PRBool doAppend = nsTableColGroupFrame::GetLastRealColGroup(this, (nsIFrame**) &lastColGroup);
02525       PRInt32 startColIndex = (lastColGroup) 
02526         ? lastColGroup->GetStartColumnIndex() + lastColGroup->GetColCount() : 0;
02527       if (doAppend) {
02528         // Append the new col group frame
02529         mColGroups.AppendFrame(nsnull, f);
02530       }
02531       else {
02532         // there is a colgroup after the last real one
02533           mColGroups.InsertFrame(nsnull, lastColGroup, f);
02534       }
02535       // Insert the colgroup and its cols into the table
02536       InsertColGroups(startColIndex, f, f);
02537     } else if (IsRowGroup(display->mDisplay)) {
02538       // Append the new row group frame to the sibling chain
02539       mFrames.AppendFrame(nsnull, f);
02540 
02541       // insert the row group and its rows into the table
02542       InsertRowGroups(f, f);
02543     } else {
02544       // Nothing special to do, just add the frame to our child list
02545       mFrames.AppendFrame(nsnull, f);
02546     }
02547 
02548     // Move to the next frame
02549     f = next;
02550   }
02551 
02552 #ifdef DEBUG_TABLE_CELLMAP
02553   printf("=== TableFrame::AppendFrames\n");
02554   Dump(PR_TRUE, PR_TRUE, PR_TRUE);
02555 #endif
02556   SetNeedStrategyInit(PR_TRUE); // XXX assume the worse
02557   AppendDirtyReflowCommand(this);
02558 
02559   return NS_OK;
02560 }
02561 
02562 NS_IMETHODIMP
02563 nsTableFrame::InsertFrames(nsIAtom*        aListName,
02564                            nsIFrame*       aPrevFrame,
02565                            nsIFrame*       aFrameList)
02566 {
02567   // Asssume there's only one frame being inserted. The problem is that
02568   // row group frames and col group frames go in separate child lists and
02569   // so if there's more than one this gets messy...
02570   // XXX The frame construction code should be separating out child frames
02571   // based on the type...
02572   NS_PRECONDITION(!aFrameList->GetNextSibling(), "expected only one child frame");
02573 
02574   // See what kind of frame we have
02575   const nsStyleDisplay* display = aFrameList->GetStyleDisplay();
02576 
02577   if (aPrevFrame) {
02578     const nsStyleDisplay* prevDisplay = aPrevFrame->GetStyleDisplay();
02579     // Make sure they belong on the same frame list
02580     if ((display->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP) !=
02581         (prevDisplay->mDisplay == NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP)) {
02582       // the previous frame is not valid, see comment at ::AppendFrames
02583       // XXXbz Using content indices here means XBL will get screwed
02584       // over...  Oh, well.
02585       nsIFrame* pseudoFrame = aFrameList;
02586       nsIContent* parentContent = GetContent();
02587       nsIContent* content;
02588       aPrevFrame = nsnull;
02589       while (pseudoFrame  && (parentContent ==
02590                               (content = pseudoFrame->GetContent()))) {
02591         pseudoFrame = pseudoFrame->GetFirstChild(nsnull);
02592       }
02593       nsCOMPtr<nsIContent> container = content->GetParent();
02594       PRInt32 newIndex = container->IndexOf(content);
02595       nsIFrame* kidFrame;
02596       PRBool isColGroup = (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP ==
02597                            display->mDisplay);
02598       if (isColGroup) {
02599         kidFrame = mColGroups.FirstChild();
02600       }
02601       else {
02602         kidFrame = mFrames.FirstChild();
02603       }
02604       // Important: need to start at a value smaller than all valid indices
02605       PRInt32 lastIndex = -1;
02606       while (kidFrame) {
02607         if (isColGroup) {
02608           nsTableColGroupType groupType =
02609             ((nsTableColGroupFrame *)kidFrame)->GetColType();
02610           if (eColGroupAnonymousCell == groupType) {
02611             continue;
02612           }
02613         }
02614         pseudoFrame = kidFrame;
02615         while (pseudoFrame  && (parentContent ==
02616                                 (content = pseudoFrame->GetContent()))) {
02617           pseudoFrame = pseudoFrame->GetFirstChild(nsnull);
02618         }
02619         PRInt32 index = container->IndexOf(content);
02620         if (index > lastIndex && index < newIndex) {
02621           lastIndex = index;
02622           aPrevFrame = kidFrame;
02623         }
02624         kidFrame = kidFrame->GetNextSibling();
02625       }
02626     }
02627   }
02628   if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
02629     // Insert the column group frame
02630     nsFrameList frames(aFrameList); // convience for getting last frame
02631     nsIFrame* lastFrame = frames.LastChild();
02632     mColGroups.InsertFrame(nsnull, aPrevFrame, aFrameList);
02633     // find the starting col index for the first new col group
02634     PRInt32 startColIndex = 0;
02635     if (aPrevFrame) {
02636       nsTableColGroupFrame* prevColGroup = 
02637         (nsTableColGroupFrame*)GetFrameAtOrBefore(this, aPrevFrame,
02638                                                   nsLayoutAtoms::tableColGroupFrame);
02639       if (prevColGroup) {
02640         startColIndex = prevColGroup->GetStartColumnIndex() + prevColGroup->GetColCount();
02641       }
02642     }
02643     InsertColGroups(startColIndex, aFrameList, lastFrame);
02644     SetNeedStrategyInit(PR_TRUE);
02645   } else if (IsRowGroup(display->mDisplay)) {
02646     nsFrameList newList(aFrameList);
02647     nsIFrame* lastSibling = newList.LastChild();
02648     // Insert the frames in the sibling chain
02649     mFrames.InsertFrame(nsnull, aPrevFrame, aFrameList);
02650 
02651     InsertRowGroups(aFrameList, lastSibling);
02652     SetNeedStrategyInit(PR_TRUE);
02653   } else {
02654     // Just insert the frame and don't worry about reflowing it
02655     mFrames.InsertFrame(nsnull, aPrevFrame, aFrameList);
02656     return NS_OK;
02657   }
02658 
02659   AppendDirtyReflowCommand(this);
02660 #ifdef DEBUG_TABLE_CELLMAP
02661   printf("=== TableFrame::InsertFrames\n");
02662   Dump(PR_TRUE, PR_TRUE, PR_TRUE);
02663 #endif
02664   return NS_OK;
02665 }
02666 
02667 NS_IMETHODIMP
02668 nsTableFrame::RemoveFrame(nsIAtom*        aListName,
02669                           nsIFrame*       aOldFrame)
02670 {
02671   // See what kind of frame we have
02672   const nsStyleDisplay* display = aOldFrame->GetStyleDisplay();
02673 
02674   if (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == display->mDisplay) {
02675     nsIFrame* nextColGroupFrame = aOldFrame->GetNextSibling();
02676     nsTableColGroupFrame* colGroup = (nsTableColGroupFrame*)aOldFrame;
02677     PRInt32 firstColIndex = colGroup->GetStartColumnIndex();
02678     PRInt32 lastColIndex  = firstColIndex + colGroup->GetColCount() - 1;
02679     mColGroups.DestroyFrame(GetPresContext(), aOldFrame);
02680     nsTableColGroupFrame::ResetColIndices(nextColGroupFrame, firstColIndex);
02681     // remove the cols from the table
02682     PRInt32 colX;
02683     for (colX = lastColIndex; colX >= firstColIndex; colX--) {
02684       nsTableColFrame* colFrame = (nsTableColFrame*)mColFrames.SafeElementAt(colX);
02685       if (colFrame) {
02686         RemoveCol(colGroup, colX, PR_TRUE, PR_FALSE);
02687       }
02688     }
02689 
02690     PRInt32 numAnonymousColsToAdd = GetColCount() - mColFrames.Count();
02691     if (numAnonymousColsToAdd > 0) {
02692       // this sets the child list, updates the col cache and cell map
02693       CreateAnonymousColFrames(numAnonymousColsToAdd,
02694                                eColAnonymousCell, PR_TRUE);
02695     }
02696 
02697     // XXX This could probably be optimized with much effort
02698     SetNeedStrategyInit(PR_TRUE);
02699     AppendDirtyReflowCommand(this);
02700   } else {
02701     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aOldFrame);
02702     if (rgFrame) {
02703       // remove the row group from the cell map
02704       nsTableCellMap* cellMap = GetCellMap();
02705       if (cellMap) {
02706         cellMap->RemoveGroupCellMap(rgFrame);
02707       }
02708 
02709       // remove the row group frame from the sibling chain
02710       mFrames.DestroyFrame(GetPresContext(), aOldFrame);
02711 
02712       if (cellMap) {
02713         cellMap->Synchronize(this);
02714         ResetRowIndices();
02715         nsRect damageArea;
02716         cellMap->RebuildConsideringCells(nsnull, nsnull, 0, 0, PR_FALSE, damageArea);
02717         MatchCellMapToColCache(cellMap);
02718       }
02719       // XXX This could probably be optimized with much effort
02720       SetNeedStrategyInit(PR_TRUE);
02721       AppendDirtyReflowCommand(this);
02722     } else {
02723       // Just remove the frame
02724       mFrames.DestroyFrame(GetPresContext(), aOldFrame);
02725       return NS_OK;
02726     }
02727     // for now, just bail and recalc all of the collapsing borders
02728     if (IsBorderCollapse()) {
02729       nsRect damageArea(0, 0, PR_MAX(1, GetColCount()), PR_MAX(1, GetRowCount()));
02730       SetBCDamageArea(damageArea);
02731     }
02732   }
02733 #ifdef DEBUG_TABLE_CELLMAP
02734   printf("=== TableFrame::RemoveFrame\n");
02735   Dump(PR_TRUE, PR_TRUE, PR_TRUE);
02736 #endif
02737 
02738   return NS_OK;
02739 }
02740 
02741 NS_METHOD 
02742 nsTableFrame::IncrementalReflow(const nsHTMLReflowState& aReflowState,
02743                                 nsReflowStatus&          aStatus)
02744 {
02745   // Constrain our reflow width to the computed table width. Note: this is
02746   // based on the width of the first-in-flow
02747   PRInt32 lastWidth = mRect.width;
02748   if (mPrevInFlow) {
02749     nsTableFrame* table = (nsTableFrame*)GetFirstInFlow();
02750     lastWidth = table->mRect.width;
02751   }
02752   nsTableReflowState state(*GetPresContext(), aReflowState, *this, eReflowReason_Incremental,
02753                            lastWidth, aReflowState.availableHeight); 
02754 
02755   // the table is a target if its path has a reflow command
02756   nsHTMLReflowCommand* command = aReflowState.path->mReflowCommand;
02757   if (command)
02758     IR_TargetIsMe(state, aStatus);
02759 
02760   // see if the chidren are targets as well
02761   nsReflowPath::iterator iter = aReflowState.path->FirstChild();
02762   nsReflowPath::iterator end  = aReflowState.path->EndChildren();
02763   for (; iter != end; ++iter)
02764     IR_TargetIsChild(state, aStatus, *iter);
02765 
02766   return NS_OK;
02767 }
02768 
02769 NS_METHOD 
02770 nsTableFrame::IR_TargetIsMe(nsTableReflowState&  aReflowState,
02771                             nsReflowStatus&      aStatus)
02772 {
02773   nsresult rv = NS_OK;
02774   aStatus = NS_FRAME_COMPLETE;
02775 
02776   switch (aReflowState.reflowState.path->mReflowCommand->Type()) {
02777     case eReflowType_StyleChanged :
02778       rv = IR_StyleChanged(aReflowState, aStatus);
02779       break;
02780     case eReflowType_ContentChanged :
02781       NS_ASSERTION(PR_FALSE, "illegal reflow type: ContentChanged");
02782       rv = NS_ERROR_ILLEGAL_VALUE;
02783       break;
02784     case eReflowType_ReflowDirty: {
02785       // reflow the dirty children
02786       nsTableReflowState reflowState(*GetPresContext(), aReflowState.reflowState, *this, eReflowReason_Initial,
02787                                      aReflowState.availSize.width, aReflowState.availSize.height); 
02788       nsIFrame* lastReflowed;
02789       PRBool reflowedAtLeastOne; 
02790       nsRect overflowArea;
02791       ReflowChildren(reflowState, PR_FALSE, PR_TRUE, aStatus,
02792                      lastReflowed, overflowArea, &reflowedAtLeastOne);
02793       if (!reflowedAtLeastOne)
02794         // XXX For now assume the worse
02795         SetNeedStrategyInit(PR_TRUE);
02796       }
02797       break;
02798     default:
02799       NS_NOTYETIMPLEMENTED("unexpected reflow command type");
02800       rv = NS_ERROR_NOT_IMPLEMENTED;
02801       break;
02802   }
02803 
02804   return rv;
02805 }
02806 
02807 NS_METHOD nsTableFrame::IR_StyleChanged(nsTableReflowState&  aReflowState,
02808                                         nsReflowStatus&      aStatus)
02809 {
02810   nsTableReflowState reflowState(*GetPresContext(), aReflowState.reflowState, *this, eReflowReason_StyleChange,
02811                                  aReflowState.availSize.width, aReflowState.availSize.height); 
02812   nsIFrame* lastReflowed;
02813   nsRect overflowArea;
02814   nsresult rv = ReflowChildren(reflowState, PR_FALSE, PR_FALSE, aStatus, lastReflowed, overflowArea);
02815   SetNeedStrategyInit(PR_TRUE);
02816   return rv;
02817 }
02818 
02819 static void
02820 DivideBCBorderSize(nscoord  aPixelSize,
02821                    nscoord& aSmallHalf,
02822                    nscoord& aLargeHalf)
02823 {
02824   aSmallHalf = aPixelSize / 2;
02825   aLargeHalf = aPixelSize - aSmallHalf;
02826 }
02827 
02828 nsMargin
02829 nsTableFrame::GetBCBorder() const
02830 {
02831   nsMargin border(0, 0, 0, 0);
02832   nsPresContext *presContext = GetPresContext();
02833   GET_PIXELS_TO_TWIPS(GetPresContext(), p2t);
02834   BCPropertyData* propData = 
02835     (BCPropertyData*)nsTableFrame::GetProperty((nsIFrame*)this, nsLayoutAtoms::tableBCProperty, PR_FALSE);
02836   if (propData) {
02837     if (eCompatibility_NavQuirks != presContext->CompatibilityMode()) {
02838       border.top += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mTopBorderWidth);
02839       border.right += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mRightBorderWidth);
02840       border.bottom += BC_BORDER_TOP_HALF_COORD(p2t, propData->mBottomBorderWidth);
02841       border.left += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mLeftBorderWidth);
02842     }
02843     else {
02844       border.top    += NSToCoordRound(p2t * (float)propData->mTopBorderWidth);
02845       border.right  += NSToCoordRound(p2t * (float)propData->mRightBorderWidth);
02846       border.bottom += NSToCoordRound(p2t * (float)propData->mBottomBorderWidth);
02847       border.left   += NSToCoordRound(p2t * (float)propData->mLeftBorderWidth);
02848     }
02849   }
02850   return border;
02851 }
02852 
02853 nsMargin
02854 nsTableFrame::GetBCMargin() const
02855 {
02856   nsMargin overflow(0, 0, 0, 0);
02857   nsPresContext* presContext = GetPresContext();
02858   GET_PIXELS_TO_TWIPS(presContext, p2t);
02859   BCPropertyData* propData =
02860     (BCPropertyData*)nsTableFrame::GetProperty((nsIFrame*)this,
02861                                                nsLayoutAtoms::tableBCProperty,
02862                                                PR_FALSE);
02863   if (propData) {
02864     if (eCompatibility_NavQuirks != presContext->CompatibilityMode()) {
02865       overflow.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
02866       overflow.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
02867       overflow.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
02868       overflow.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
02869     }
02870   }
02871   return overflow;
02872 }
02873 static
02874 void GetSeparateModelBorderPadding(const nsHTMLReflowState* aReflowState,
02875                                    nsStyleContext&          aStyleContext,
02876                                    nsMargin&                aBorderPadding)
02877 {
02878   // XXXbz Either we _do_ have a reflow state and then we can use its
02879   // mComputedBorderPadding or we don't and then we get the padding
02880   // wrong!
02881   const nsStyleBorder* border = aStyleContext.GetStyleBorder();
02882   aBorderPadding = border->GetBorder();
02883   if (aReflowState) {
02884     aBorderPadding += aReflowState->mComputedPadding;
02885   }
02886 }
02887 
02888 nsMargin 
02889 nsTableFrame::GetChildAreaOffset(const nsHTMLReflowState* aReflowState) const
02890 {
02891   nsMargin offset(0,0,0,0);
02892   if (IsBorderCollapse()) {
02893     nsPresContext* presContext = GetPresContext();
02894     if (eCompatibility_NavQuirks == presContext->CompatibilityMode()) {
02895       nsTableFrame* firstInFlow = (nsTableFrame*)GetFirstInFlow(); if (!firstInFlow) ABORT1(offset);
02896       GET_PIXELS_TO_TWIPS(presContext, p2t);
02897       BCPropertyData* propData = 
02898         (BCPropertyData*)nsTableFrame::GetProperty((nsIFrame*)firstInFlow, nsLayoutAtoms::tableBCProperty, PR_FALSE);
02899       if (!propData) ABORT1(offset);
02900 
02901       offset.top += BC_BORDER_TOP_HALF_COORD(p2t, propData->mTopBorderWidth);
02902       offset.right += BC_BORDER_RIGHT_HALF_COORD(p2t, propData->mRightBorderWidth);
02903       offset.bottom += BC_BORDER_BOTTOM_HALF_COORD(p2t, propData->mBottomBorderWidth);
02904       offset.left += BC_BORDER_LEFT_HALF_COORD(p2t, propData->mLeftBorderWidth);
02905     }
02906   }
02907   else {
02908     GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
02909   }
02910   return offset;
02911 }
02912 
02913 nsMargin 
02914 nsTableFrame::GetContentAreaOffset(const nsHTMLReflowState* aReflowState) const
02915 {
02916   nsMargin offset(0,0,0,0);
02917   if (IsBorderCollapse()) {
02918     offset = GetBCBorder();
02919   }
02920   else {
02921     GetSeparateModelBorderPadding(aReflowState, *mStyleContext, offset);
02922   }
02923   return offset;
02924 }
02925 
02926 // Recovers the reflow state to what it should be if aKidFrame is about to be 
02927 // reflowed. Restores y, footerFrame, firstBodySection and availSize.height (if
02928 // the height is constrained)
02929 nsresult
02930 nsTableFrame::RecoverState(nsTableReflowState& aReflowState,
02931                            nsIFrame*           aKidFrame)
02932 {
02933   nsMargin borderPadding = GetChildAreaOffset(&aReflowState.reflowState);
02934   aReflowState.y = borderPadding.top;
02935 
02936   nscoord cellSpacingY = GetCellSpacingY();
02937   // Get the ordered children and find aKidFrame in the list
02938   nsAutoVoidArray rowGroups;
02939   PRUint32 numRowGroups;
02940   OrderRowGroups(rowGroups, numRowGroups, &aReflowState.firstBodySection);
02941   
02942   // Walk the list of children looking for aKidFrame
02943   for (PRUint32 childX = 0; childX < numRowGroups; childX++) {
02944     nsIFrame* childFrame = (nsIFrame*)rowGroups.ElementAt(childX);
02945     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(childFrame);
02946     if (!rgFrame) continue; // skip foreign frame types
02947    
02948     // If this is a footer row group, remember it
02949     const nsStyleDisplay* display = rgFrame->GetStyleDisplay();
02950 
02951     // We only allow a single footer frame
02952     if ((NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == display->mDisplay) &&
02953         !aReflowState.footerFrame) {
02954       aReflowState.footerFrame = childFrame;    
02955     }
02956     else {
02957       if ((NS_STYLE_DISPLAY_TABLE_ROW_GROUP == display->mDisplay) &&
02958           !aReflowState.firstBodySection) {
02959         aReflowState.firstBodySection = childFrame;
02960       }
02961     }
02962     aReflowState.y += cellSpacingY;
02963     
02964     // See if this is the frame we're looking for
02965     if (childFrame == aKidFrame) {
02966       break;
02967     }
02968 
02969     // Get the frame's height
02970     nsSize kidSize = childFrame->GetSize();
02971     
02972     // If our height is constrained then update the available height. Do
02973     // this for all frames including the footer frame
02974     if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
02975       aReflowState.availSize.height -= kidSize.height;
02976     }
02977 
02978     // Update the running y-offset. Don't do this for the footer frame
02979     if (childFrame != aReflowState.footerFrame) {
02980       aReflowState.y += kidSize.height;
02981     }
02982   }
02983 
02984   return NS_OK;
02985 }
02986 
02987 void
02988 nsTableFrame::InitChildReflowState(nsHTMLReflowState& aReflowState)                                    
02989 {
02990   nsMargin collapseBorder;
02991   nsMargin padding(0,0,0,0);
02992   nsMargin* pCollapseBorder = nsnull;
02993   nsPresContext* presContext = GetPresContext();
02994   if (IsBorderCollapse()) {
02995     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aReflowState.frame);
02996     if (rgFrame) {
02997       GET_PIXELS_TO_TWIPS(presContext, p2t);
02998       pCollapseBorder = rgFrame->GetBCBorderWidth(p2t, collapseBorder);
02999     }
03000   }
03001   aReflowState.Init(presContext, -1, -1, pCollapseBorder, &padding);
03002 }
03003 
03004 NS_METHOD 
03005 nsTableFrame::IR_TargetIsChild(nsTableReflowState&  aReflowState,
03006                                nsReflowStatus&      aStatus,
03007                                nsIFrame*            aNextFrame)
03008 
03009 {
03010   nsresult rv;
03011   // Recover the state as if aNextFrame is about to be reflowed
03012   RecoverState(aReflowState, aNextFrame);
03013 
03014   // Remember the old rect
03015   nsRect oldKidRect = aNextFrame->GetRect();
03016 
03017   // Pass along the reflow command, don't request a max element size, rows will do that
03018   nsHTMLReflowMetrics desiredSize(PR_FALSE);
03019 
03020   nsSize kidAvailSize(aReflowState.availSize);
03021   nsPresContext* presContext = GetPresContext();
03022   nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, aNextFrame, 
03023                                    kidAvailSize, aReflowState.reason);
03024   InitChildReflowState(kidReflowState);
03025 
03026   rv = ReflowChild(aNextFrame, presContext, desiredSize, kidReflowState,
03027                    aReflowState.x, aReflowState.y, 0, aStatus);
03028 
03029   // Place the row group frame. Don't use PlaceChild(), because it moves
03030   // the footer frame as well. We'll adjust the footer frame later on in
03031   // AdjustSiblingsAfterReflow()
03032   nsRect  kidRect(aReflowState.x, aReflowState.y, desiredSize.width, desiredSize.height);
03033   FinishReflowChild(aNextFrame, presContext, nsnull, desiredSize, aReflowState.x, aReflowState.y, 0);
03034 
03035   // Adjust the running y-offset
03036   aReflowState.y += desiredSize.height + GetCellSpacingY();
03037 
03038   // If our height is constrained, then update the available height
03039   if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
03040     aReflowState.availSize.height -= desiredSize.height;
03041   }
03042 
03043   // If the column width info is valid, then adjust the row group frames
03044   // that follow. Otherwise, return and we'll recompute the column widths
03045   // and reflow all the row group frames
03046   if (!NeedsReflow(aReflowState.reflowState)) {
03047     // If the row group frame changed height, then damage the horizontal strip
03048     // that was either added or went away
03049     if (desiredSize.height != oldKidRect.height) {
03050       nsRect dirtyRect;
03051       dirtyRect.x = 0;
03052       dirtyRect.y = PR_MIN(oldKidRect.YMost(), kidRect.YMost());
03053       dirtyRect.width = mRect.width;
03054       dirtyRect.height = PR_MAX(oldKidRect.YMost(), kidRect.YMost()) - dirtyRect.y;
03055       Invalidate(dirtyRect);
03056     }
03057 
03058     // Adjust the row groups that follow
03059     AdjustSiblingsAfterReflow(aReflowState, aNextFrame, 
03060                               desiredSize.height - oldKidRect.height);
03061 
03062     // recover the overflow area from all children
03063     desiredSize.mOverflowArea = nsRect(0, 0, desiredSize.width, desiredSize.height);
03064     for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
03065       ConsiderChildOverflow(desiredSize.mOverflowArea, kidFrame);
03066     }  
03067     FinishAndStoreOverflow(&desiredSize.mOverflowArea,
03068                            nsSize(desiredSize.width, desiredSize.height));
03069   }
03070   return rv;
03071 }
03072 
03073 // Position and size aKidFrame and update our reflow state. The origin of
03074 // aKidRect is relative to the upper-left origin of our frame
03075 void nsTableFrame::PlaceChild(nsTableReflowState&  aReflowState,
03076                               nsIFrame*            aKidFrame,
03077                               nsHTMLReflowMetrics& aKidDesiredSize)
03078 {
03079   
03080   // Place and size the child
03081   FinishReflowChild(aKidFrame, GetPresContext(), nsnull, aKidDesiredSize,
03082                     aReflowState.x, aReflowState.y, 0);
03083 
03084   // Adjust the running y-offset
03085   aReflowState.y += aKidDesiredSize.height;
03086 
03087   // If our height is constrained, then update the available height
03088   if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
03089     aReflowState.availSize.height -= aKidDesiredSize.height;
03090   }
03091 
03092   const nsStyleDisplay* childDisplay = aKidFrame->GetStyleDisplay();
03093 
03094   // We only allow a single footer frame, and the footer frame must occur before
03095   // any body section row groups
03096   if ((NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == childDisplay->mDisplay) &&
03097       !aReflowState.footerFrame && !aReflowState.firstBodySection) {
03098     aReflowState.footerFrame = aKidFrame;
03099   }
03100   else if (aReflowState.footerFrame) {
03101     // put the non footer where the footer was
03102     nsPoint origin = aReflowState.footerFrame->GetPosition();
03103     aKidFrame->SetPosition(origin);
03104 
03105     // put the footer below the non footer
03106     origin.y = aReflowState.y - aReflowState.footerFrame->GetSize().height;
03107     aReflowState.footerFrame->SetPosition(origin);
03108   }
03109 }
03110 
03111 void
03112 nsTableFrame::OrderRowGroups(nsVoidArray&           aChildren,
03113                              PRUint32&              aNumRowGroups,
03114                              nsIFrame**             aFirstBody,
03115                              nsTableRowGroupFrame** aHead,
03116                              nsTableRowGroupFrame** aFoot) const
03117 {
03118   aChildren.Clear();
03119   nsIFrame* head = nsnull;
03120   nsIFrame* foot = nsnull;
03121   // initialize out parameters, if present
03122   if (aFirstBody) *aFirstBody = nsnull;
03123   if (aHead)      *aHead      = nsnull;
03124   if (aFoot)      *aFoot      = nsnull;
03125   
03126   nsIFrame* kidFrame = mFrames.FirstChild();
03127   nsAutoVoidArray nonRowGroups;
03128   // put the tbodies first, and the non row groups last
03129   while (kidFrame) {
03130     const nsStyleDisplay* kidDisplay = kidFrame->GetStyleDisplay();
03131     if (IsRowGroup(kidDisplay->mDisplay)) {
03132       switch(kidDisplay->mDisplay) {
03133       case NS_STYLE_DISPLAY_TABLE_HEADER_GROUP:
03134         if (head) { // treat additional thead like tbody
03135           aChildren.AppendElement(kidFrame);
03136         }
03137         else {
03138           head = kidFrame;
03139           if (aHead) {
03140             *aHead = (nsTableRowGroupFrame*)head;
03141           }
03142         }
03143         break;
03144       case NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP:
03145         if (foot) {
03146           aChildren.AppendElement(kidFrame);
03147         }
03148         else {
03149           foot = kidFrame;
03150           if (aFoot) {
03151             *aFoot = (nsTableRowGroupFrame*)foot;
03152           }
03153         }
03154         break;
03155       default:
03156         aChildren.AppendElement(kidFrame);
03157         if (aFirstBody && !*aFirstBody) {
03158           *aFirstBody = kidFrame;
03159         }
03160       }
03161     }
03162     else {
03163       nonRowGroups.AppendElement(kidFrame);
03164     }
03165     // Get the next sibling but skip it if it's also the next-in-flow, since
03166     // a next-in-flow will not be part of the current table.
03167     while (kidFrame) {
03168       nsIFrame* nif = kidFrame->GetNextInFlow();
03169       kidFrame = kidFrame->GetNextSibling();
03170       if (kidFrame != nif) 
03171         break;
03172     }
03173   }
03174   aNumRowGroups = aChildren.Count();
03175   // put the thead first
03176   if (head) {
03177     aChildren.InsertElementAt(head, 0);
03178     aNumRowGroups++;
03179   }
03180   // put the tfoot after the last tbody
03181   if (foot) {
03182     aChildren.InsertElementAt(foot, aNumRowGroups);
03183     aNumRowGroups++;
03184   }
03185   // put the non row groups at the end
03186   PRInt32 numNonRowGroups = nonRowGroups.Count();
03187   for (PRInt32 i = 0; i < numNonRowGroups; i++) {
03188     aChildren.AppendElement(nonRowGroups.ElementAt(i));
03189   }
03190 }
03191 
03192 static PRBool
03193 IsRepeatable(nsTableRowGroupFrame& aHeaderOrFooter,
03194              nscoord               aPageHeight)
03195 {
03196   return aHeaderOrFooter.GetSize().height < (aPageHeight / 4);
03197 }
03198 
03199 // Reflow the children based on the avail size and reason in aReflowState
03200 // update aReflowMetrics a aStatus
03201 NS_METHOD 
03202 nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
03203                              PRBool              aDoColGroups,
03204                              PRBool              aDirtyOnly,
03205                              nsReflowStatus&     aStatus,
03206                              nsIFrame*&          aLastChildReflowed,
03207                              nsRect&             aOverflowArea,
03208                              PRBool*             aReflowedAtLeastOne)
03209 {
03210   aStatus = NS_FRAME_COMPLETE;
03211   aLastChildReflowed = nsnull;
03212 
03213   nsIFrame* prevKidFrame = nsnull;
03214   nsresult  rv = NS_OK;
03215   nscoord   cellSpacingY = GetCellSpacingY();
03216 
03217   nsPresContext* presContext = GetPresContext();
03218   PRBool isPaginated = presContext->IsPaginated();
03219 
03220   aOverflowArea = nsRect (0, 0, 0, 0);
03221   
03222   nsAutoVoidArray rowGroups;
03223   PRUint32 numRowGroups;
03224   nsTableRowGroupFrame *thead, *tfoot;
03225   OrderRowGroups(rowGroups, numRowGroups, &aReflowState.firstBodySection, &thead, &tfoot);
03226   PRBool haveReflowedRowGroup = PR_FALSE;
03227   PRBool pageBreak = PR_FALSE;
03228   for (PRUint32 childX = 0; ((PRInt32)childX) < rowGroups.Count(); childX++) {
03229     nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(childX);
03230     // Get the frame state bits
03231     // See if we should only reflow the dirty child frames
03232     PRBool doReflowChild = PR_TRUE;
03233     if (aDirtyOnly && ((kidFrame->GetStateBits() & NS_FRAME_IS_DIRTY) == 0)) {
03234       doReflowChild = PR_FALSE;
03235     }
03236 
03237     if (doReflowChild) {
03238       if (pageBreak) {
03239         PushChildren(rowGroups, childX);
03240         aStatus = NS_FRAME_NOT_COMPLETE;
03241         break;
03242       }
03243 
03244       nsSize kidAvailSize(aReflowState.availSize);
03245       // if the child is a tbody in paginated mode reduce the height by a repeated footer
03246       nsIFrame* repeatedFooter = nsnull;
03247       nscoord repeatedFooterHeight = 0;
03248       if (isPaginated && (NS_UNCONSTRAINEDSIZE != kidAvailSize.height)) {
03249         if (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == kidFrame->GetStyleDisplay()->mDisplay) { // the child is a tbody
03250           nsIFrame* lastChild = (nsIFrame*)rowGroups.ElementAt(numRowGroups - 1);
03251           if (NS_STYLE_DISPLAY_TABLE_FOOTER_GROUP == lastChild->GetStyleDisplay()->mDisplay) { // the last child is a tfoot
03252             if (((nsTableRowGroupFrame*)lastChild)->IsRepeatable()) {
03253               repeatedFooterHeight = lastChild->GetSize().height;
03254               if (repeatedFooterHeight + cellSpacingY < kidAvailSize.height) {
03255                 repeatedFooter = lastChild;
03256                 kidAvailSize.height -= repeatedFooterHeight + cellSpacingY;
03257               }
03258             }
03259           }
03260         }
03261       }
03262 
03263       nsHTMLReflowMetrics desiredSize(PR_FALSE);
03264       desiredSize.width = desiredSize.height = desiredSize.ascent = desiredSize.descent = 0;
03265   
03266       if (childX < numRowGroups) {  
03267         // Reflow the child into the available space
03268         nsHTMLReflowState  kidReflowState(presContext, aReflowState.reflowState, kidFrame, 
03269                                           kidAvailSize, aReflowState.reason);
03270         InitChildReflowState(kidReflowState);
03271         // XXX fix up bad mComputedWidth for scroll frame
03272         kidReflowState.mComputedWidth = PR_MAX(kidReflowState.mComputedWidth, 0);
03273   
03274         // If this isn't the first row group, then we can't be at the top of the page
03275         if (childX > 0) {
03276           kidReflowState.mFlags.mIsTopOfPage = PR_FALSE;
03277         }
03278         aReflowState.y += cellSpacingY;
03279         if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
03280           aReflowState.availSize.height -= cellSpacingY;
03281         }
03282         // record the presence of a next in flow, it might get destroyed so we
03283         // need to reorder the row group array
03284         nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
03285         PRBool reorder = PR_FALSE;
03286         if (kidFrame->GetNextInFlow())
03287           reorder = PR_TRUE;
03288         
03289         rv = ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
03290                          aReflowState.x, aReflowState.y, 0, aStatus);
03291         haveReflowedRowGroup = PR_TRUE;
03292         
03293         if (reorder) {
03294           // reorder row groups the reflow may have changed the nextinflows
03295           OrderRowGroups(rowGroups, numRowGroups,
03296                          &aReflowState.firstBodySection, &thead, &tfoot);
03297           for (childX = 0; childX < numRowGroups; childX++) {
03298             if (kidFrame == (nsIFrame*)rowGroups.ElementAt(childX))
03299               break;
03300           }
03301         }
03302         // see if the rowgroup did not fit on this page might be pushed on
03303         // the next page
03304         if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
03305             (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight) &&
03306             kidReflowState.availableHeight < desiredSize.height) {
03307           // if we are on top of the page place with dataloss
03308           if (kidReflowState.mFlags.mIsTopOfPage) {
03309             if (childX+1 < numRowGroups) {
03310               nsIFrame* nextRowGroupFrame = (nsIFrame*) rowGroups.ElementAt(childX +1);
03311               if (nextRowGroupFrame) {
03312                 PlaceChild(aReflowState, kidFrame, desiredSize);
03313                 aStatus = NS_FRAME_NOT_COMPLETE;
03314                 PushChildren(rowGroups, childX + 1);
03315                 aLastChildReflowed = kidFrame;
03316                 break;
03317               }
03318             }
03319           }
03320           else { // we are not on top, push this rowgroup onto the next page
03321             if (prevKidFrame) { // we had a rowgroup before so push this
03322               aStatus = NS_FRAME_NOT_COMPLETE;
03323               PushChildren(rowGroups, childX);
03324               aLastChildReflowed = prevKidFrame;
03325               break;
03326             }
03327           }
03328         }
03329 
03330         aLastChildReflowed   = kidFrame;
03331 
03332         pageBreak = PR_FALSE;
03333         // see if there is a page break after this row group or before the next one
03334         if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated && 
03335             (NS_UNCONSTRAINEDSIZE != kidReflowState.availableHeight)) {
03336           nsIFrame* nextKid = (childX + 1 < numRowGroups) ? (nsIFrame*)rowGroups.ElementAt(childX + 1) : nsnull;
03337           pageBreak = PageBreakAfter(*kidFrame, nextKid);
03338         }
03339 
03340         // Place the child
03341         PlaceChild(aReflowState, kidFrame, desiredSize);
03342   
03343         // Remember where we just were in case we end up pushing children
03344         prevKidFrame = kidFrame;
03345  
03346         // Special handling for incomplete children
03347         if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {         
03348           kidNextInFlow = kidFrame->GetNextInFlow();
03349           if (!kidNextInFlow) {
03350             // The child doesn't have a next-in-flow so create a continuing
03351             // frame. This hooks the child into the flow
03352             nsIFrame*     continuingFrame;
03353 
03354             rv = presContext->PresShell()->FrameConstructor()->
03355               CreateContinuingFrame(presContext, kidFrame, this,
03356                                     &continuingFrame);
03357             if (NS_FAILED(rv)) {
03358               aStatus = NS_FRAME_COMPLETE;
03359               break;
03360             }
03361             // Add the continuing frame to the sibling list
03362             continuingFrame->SetNextSibling(kidFrame->GetNextSibling());
03363             kidFrame->SetNextSibling(continuingFrame);
03364             // Update rowGroups with the new rowgroup, just as it
03365             // would have been if we had called OrderRowGroups
03366             // again. Note that rowGroups doesn't get used again after
03367             // we PushChildren below, anyway.
03368             rowGroups.InsertElementAt(continuingFrame, childX + 1);
03369           }
03370           else {
03371             // put the nextinflow so that it will get pushed
03372             rowGroups.InsertElementAt(kidNextInFlow, childX + 1);
03373           }
03374 
03375           // We've used up all of our available space so push the remaining
03376           // children to the next-in-flow
03377           nsIFrame* nextSibling = kidFrame->GetNextSibling();
03378           if (nsnull != nextSibling) {
03379             PushChildren(rowGroups, childX + 1);
03380           }
03381           if (repeatedFooter) {
03382             kidAvailSize.height = repeatedFooterHeight;
03383             nsHTMLReflowState footerReflowState(presContext, aReflowState.reflowState, repeatedFooter, 
03384                                                 kidAvailSize, aReflowState.reason);
03385             InitChildReflowState(footerReflowState);
03386             aReflowState.y += cellSpacingY;
03387             nsReflowStatus footerStatus;
03388             rv = ReflowChild(repeatedFooter, presContext, desiredSize, footerReflowState,
03389                              aReflowState.x, aReflowState.y, 0, footerStatus);
03390             PlaceChild(aReflowState, repeatedFooter, desiredSize);
03391           }
03392           break;
03393         }
03394       }
03395     }
03396     else if (childX < numRowGroups) { // it is a row group but isn't being reflowed
03397       nsRect kidRect = kidFrame->GetRect();
03398       if (haveReflowedRowGroup) { 
03399         if (kidRect.y != aReflowState.y) {
03400           Invalidate(kidRect); // invalidate the old position
03401           kidRect.y = aReflowState.y;
03402           kidFrame->SetRect(kidRect);        // move to the new position
03403           Invalidate(kidRect); // invalidate the new position
03404         }
03405       }
03406       aReflowState.y += cellSpacingY + kidRect.height;
03407     }
03408     ConsiderChildOverflow(aOverflowArea, kidFrame);
03409   }
03410   
03411   // if required, give the colgroups their initial reflows
03412   if (aDoColGroups) {
03413     nsHTMLReflowMetrics kidMet(PR_FALSE);
03414     for (nsIFrame* kidFrame = mColGroups.FirstChild(); kidFrame;
03415          kidFrame = kidFrame->GetNextSibling()) {
03416       nsHTMLReflowState kidReflowState(presContext, aReflowState.reflowState, kidFrame,
03417                                        aReflowState.availSize, aReflowState.reason);
03418       nsReflowStatus cgStatus;
03419       ReflowChild(kidFrame, presContext, kidMet, kidReflowState, 0, 0, 0, cgStatus);
03420       FinishReflowChild(kidFrame, presContext, nsnull, kidMet, 0, 0, 0);
03421     }
03422     SetHaveReflowedColGroups(PR_TRUE);
03423   }
03424 
03425   // set the repeatablility of headers and footers in the original table during its first reflow
03426   // the repeatability of header and footers on continued tables is handled when they are created
03427   if (isPaginated && !mPrevInFlow && (NS_UNCONSTRAINEDSIZE == aReflowState.availSize.height)) {
03428     nsRect actualRect;
03429     nsRect adjRect;
03430     presContext->GetPageDim(&actualRect, &adjRect);
03431     // don't repeat the thead or tfoot unless it is < 25% of the page height
03432     if (thead) {
03433       thead->SetRepeatable(IsRepeatable(*thead, actualRect.height));
03434     }
03435     if (tfoot) {
03436       tfoot->SetRepeatable(IsRepeatable(*tfoot, actualRect.height));
03437     }
03438   }
03439 
03440   if (aReflowedAtLeastOne) {
03441     *aReflowedAtLeastOne = haveReflowedRowGroup;
03442   }
03443   return rv;
03444 }
03445 
03452 // use the cell map to determine which cell is in which column.
03453 void nsTableFrame::BalanceColumnWidths(const nsHTMLReflowState& aReflowState)
03454 {
03455   NS_ASSERTION(!mPrevInFlow, "never ever call me on a continuing frame!");
03456 
03457   // fixed-layout tables need to reinitialize the layout strategy. When there are scroll bars
03458   // reflow gets called twice and the 2nd time has the correct space available.
03459   // XXX this is very bad and needs to be changed
03460   if (!IsAutoLayout()) {
03461     mTableLayoutStrategy->Initialize(aReflowState);
03462   }
03463 
03464   // need to figure out the overall table width constraint
03465   // default case, get 100% of available space
03466 
03467   mTableLayoutStrategy->BalanceColumnWidths(aReflowState);
03468   //Dump(PR_TRUE, PR_TRUE);
03469   SetNeedStrategyBalance(PR_FALSE);                    // we have just balanced
03470   // cache the min, desired, and preferred widths
03471   nscoord minWidth, prefWidth;
03472   CalcMinAndPreferredWidths(aReflowState, PR_FALSE, minWidth, prefWidth);
03473   SetMinWidth(minWidth); 
03474   nscoord desWidth = CalcDesiredWidth(aReflowState);
03475   SetDesiredWidth(desWidth);          
03476   SetPreferredWidth(prefWidth); 
03477 
03478 }
03479 
03480 // This width is based on the column widths array of the table.
03481 // sum the width of each column and add in table insets
03482 nscoord 
03483 nsTableFrame::CalcDesiredWidth(const nsHTMLReflowState& aReflowState)
03484 {
03485   NS_ASSERTION(!mPrevInFlow, "never ever call me on a continuing frame!");
03486   nsTableCellMap* cellMap = GetCellMap();
03487   if (!cellMap) {
03488     NS_ASSERTION(PR_FALSE, "never ever call me until the cell map is built!");
03489     return 0;
03490   }
03491 
03492   nscoord cellSpacing = GetCellSpacingX();
03493   PRInt32 tableWidth  = 0;
03494 
03495   PRInt32 numCols = GetColCount();
03496   for (PRInt32 colIndex = 0; colIndex < numCols; colIndex++) {
03497     nscoord totalColWidth = GetColumnWidth(colIndex);
03498     if (GetNumCellsOriginatingInCol(colIndex) > 0) { // skip degenerate cols
03499       totalColWidth += cellSpacing;           // add cell spacing to left of col
03500     }
03501     tableWidth += totalColWidth;
03502   }
03503 
03504   if (numCols > 0)
03505     tableWidth += cellSpacing; // add last cellspacing
03506 
03507   PRBool isPctWidth = PR_FALSE;
03508   nscoord compWidth = aReflowState.mComputedWidth;
03509   if (!IsAutoWidth(&isPctWidth) &&
03510       (NS_UNCONSTRAINEDSIZE != compWidth) && !isPctWidth)
03511     tableWidth = PR_MAX(tableWidth, compWidth);
03512 
03513   // Add the width between the border edge and the child area
03514   nsMargin childOffset = GetChildAreaOffset(&aReflowState);
03515   tableWidth += childOffset.left + childOffset.right;
03516 
03517   return tableWidth;
03518 }
03519 
03520 
03521 void 
03522 nsTableFrame::CalcDesiredHeight(const nsHTMLReflowState& aReflowState, nsHTMLReflowMetrics& aDesiredSize) 
03523 {
03524   nsTableCellMap* cellMap = GetCellMap();
03525   if (!cellMap) {
03526     NS_ASSERTION(PR_FALSE, "never ever call me until the cell map is built!");
03527     aDesiredSize.height = 0;
03528     return;
03529   }
03530   nscoord  cellSpacingY = GetCellSpacingY();
03531   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
03532 
03533   // get the natural height based on the last child's (row group or scroll frame) rect
03534   nsAutoVoidArray rowGroups;
03535   PRUint32 numRowGroups;
03536   OrderRowGroups(rowGroups, numRowGroups, nsnull);
03537   if (numRowGroups <= 0) {
03538     // tables can be used as rectangular items without content
03539     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
03540     if ((NS_UNCONSTRAINEDSIZE != tableSpecifiedHeight) &&
03541         (tableSpecifiedHeight > 0) &&
03542         eCompatibility_NavQuirks != GetPresContext()->CompatibilityMode()) {
03543           // empty tables should not have a size in quirks mode
03544       aDesiredSize.height = tableSpecifiedHeight;
03545     } 
03546     else
03547       aDesiredSize.height = 0;
03548     return;
03549   }
03550   PRInt32 rowCount = cellMap->GetRowCount();
03551   PRInt32 colCount = cellMap->GetColCount();
03552   nscoord desiredHeight = borderPadding.top + borderPadding.bottom;
03553   if (rowCount > 0 && colCount > 0) {
03554     desiredHeight += cellSpacingY;
03555     for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
03556       nsIFrame* rg = (nsIFrame*)rowGroups.ElementAt(rgX);
03557       if (rg) {
03558         desiredHeight += rg->GetSize().height + cellSpacingY;
03559       }
03560     }
03561   }
03562 
03563   // see if a specified table height requires dividing additional space to rows
03564   if (!mPrevInFlow) {
03565     nscoord tableSpecifiedHeight = CalcBorderBoxHeight(aReflowState);
03566     if ((tableSpecifiedHeight > 0) && 
03567         (tableSpecifiedHeight != NS_UNCONSTRAINEDSIZE) &&
03568         (tableSpecifiedHeight > desiredHeight)) {
03569       // proportionately distribute the excess height to unconstrained rows in each
03570       // unconstrained row group.We don't need to do this if it's an unconstrained reflow
03571       if (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth) { 
03572         DistributeHeightToRows(aReflowState, tableSpecifiedHeight - desiredHeight);
03573         // this might have changed the overflow area incorporate the childframe overflow area.
03574         for (nsIFrame* kidFrame = mFrames.FirstChild(); kidFrame; kidFrame = kidFrame->GetNextSibling()) {
03575           ConsiderChildOverflow(aDesiredSize.mOverflowArea, kidFrame);
03576         } 
03577       }
03578       desiredHeight = tableSpecifiedHeight;
03579     }
03580   }
03581   aDesiredSize.height = desiredHeight;
03582 }
03583 
03584 static
03585 void ResizeCells(nsTableFrame&            aTableFrame,
03586                  nsPresContext*          aPresContext,
03587                  const nsHTMLReflowState& aReflowState)
03588 {
03589   nsAutoVoidArray rowGroups;
03590   PRUint32 numRowGroups;
03591   aTableFrame.OrderRowGroups(rowGroups, numRowGroups, nsnull);
03592   nsHTMLReflowMetrics tableDesiredSize(PR_FALSE);
03593   nsRect tableRect = aTableFrame.GetRect();
03594   tableDesiredSize.width = tableRect.width;
03595   tableDesiredSize.height = tableRect.height;
03596   tableDesiredSize.mOverflowArea = nsRect(0, 0, tableRect.width,
03597                                           tableRect.height);
03598 
03599   for (PRUint32 rgX = 0; (rgX < numRowGroups); rgX++) {
03600     nsTableRowGroupFrame* rgFrame = aTableFrame.GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
03601    
03602     nsRect rowGroupRect = rgFrame->GetRect();
03603     nsHTMLReflowMetrics groupDesiredSize(PR_FALSE);
03604     groupDesiredSize.width = rowGroupRect.width;
03605     groupDesiredSize.height = rowGroupRect.height;
03606     groupDesiredSize.mOverflowArea = nsRect(0, 0, groupDesiredSize.width,
03607                                       groupDesiredSize.height);
03608     nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
03609     while (rowFrame) {
03610       rowFrame->DidResize(aPresContext, aReflowState);
03611       rgFrame->ConsiderChildOverflow(groupDesiredSize.mOverflowArea, rowFrame);
03612       rowFrame = rowFrame->GetNextRow();
03613     }
03614     rgFrame->FinishAndStoreOverflow(&groupDesiredSize.mOverflowArea,
03615                                     nsSize(groupDesiredSize.width, groupDesiredSize.height));
03616     // make the coordinates of |desiredSize.mOverflowArea| incorrect
03617     // since it's about to go away:
03618     groupDesiredSize.mOverflowArea.MoveBy(rgFrame->GetPosition());
03619     tableDesiredSize.mOverflowArea.UnionRect(tableDesiredSize.mOverflowArea, groupDesiredSize.mOverflowArea);
03620   }
03621   aTableFrame.FinishAndStoreOverflow(&tableDesiredSize.mOverflowArea,
03622                                      nsSize(tableDesiredSize.width, tableDesiredSize.height));
03623 }
03624 
03625 void
03626 nsTableFrame::DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
03627                                      nscoord                  aAmount)
03628 { 
03629   nsPresContext *presContext = GetPresContext();
03630   float p2t;
03631   p2t = presContext->PixelsToTwips();
03632 
03633   nscoord cellSpacingY = GetCellSpacingY();
03634 
03635   nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
03636   
03637   nsVoidArray rowGroups;
03638   PRUint32 numRowGroups;
03639   OrderRowGroups(rowGroups, numRowGroups, nsnull);
03640 
03641   nscoord amountUsed = 0;
03642   // distribute space to each pct height row whose row group doesn't have a computed 
03643   // height, and base the pct on the table height. If the row group had a computed 
03644   // height, then this was already done in nsTableRowGroupFrame::CalculateRowHeights
03645   nscoord pctBasis = aReflowState.mComputedHeight - (GetCellSpacingY() * (GetRowCount() + 1));
03646   nscoord yOriginRG = borderPadding.top + GetCellSpacingY();
03647   nscoord yEndRG = yOriginRG;
03648   PRUint32 rgX;
03649   for (rgX = 0; (rgX < numRowGroups); rgX++) {
03650     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
03651     nscoord amountUsedByRG = 0;
03652     nscoord yOriginRow = 0;
03653     nsRect rgRect = rgFrame->GetRect();
03654     if (rgFrame && !rgFrame->HasStyleHeight()) {
03655       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
03656       while (rowFrame) {
03657         nsRect rowRect = rowFrame->GetRect();
03658         if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
03659           nscoord pctHeight = nsTableFrame::RoundToPixel(rowFrame->GetHeight(pctBasis), p2t);
03660           nscoord amountForRow = PR_MIN(aAmount - amountUsed, pctHeight - rowRect.height);
03661           if (amountForRow > 0) {
03662             rowRect.height += amountForRow;
03663             rowFrame->SetRect(rowRect);
03664             yOriginRow += rowRect.height + cellSpacingY;
03665             yEndRG += rowRect.height + cellSpacingY;
03666             amountUsed += amountForRow;
03667             amountUsedByRG += amountForRow;
03668             //rowFrame->DidResize(aPresContext, aReflowState);        
03669             nsTableFrame::RePositionViews(rowFrame);
03670           }
03671         }
03672         else {
03673           if (amountUsed > 0) {
03674             rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
03675             nsTableFrame::RePositionViews(rowFrame);
03676           }
03677           yOriginRow += rowRect.height + cellSpacingY;
03678           yEndRG += rowRect.height + cellSpacingY;
03679         }
03680         rowFrame = rowFrame->GetNextRow();
03681       }
03682       if (amountUsed > 0) {
03683         rgRect.y = yOriginRG;
03684         rgRect.height += amountUsedByRG;
03685         rgFrame->SetRect(rgRect);
03686       }
03687     }
03688     else if (amountUsed > 0) {
03689       rgFrame->SetPosition(nsPoint(0, yOriginRG));
03690       // Make sure child views are properly positioned
03691       nsTableFrame::RePositionViews(rgFrame);
03692     }
03693     yOriginRG = yEndRG;
03694   }
03695 
03696   if (amountUsed >= aAmount) {
03697     ResizeCells(*this, presContext, aReflowState);
03698     return;
03699   }
03700 
03701   // get the first row without a style height where its row group has an unconstrianed height
03702   nsTableRowGroupFrame* firstUnStyledRG  = nsnull;
03703   nsTableRowFrame*      firstUnStyledRow = nsnull;
03704   for (rgX = 0; (rgX < numRowGroups) && !firstUnStyledRG; rgX++) {
03705     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
03706     if (rgFrame && !rgFrame->HasStyleHeight()) {
03707       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
03708       while (rowFrame) {
03709         if (!rowFrame->HasStyleHeight()) {
03710           firstUnStyledRG = rgFrame;
03711           firstUnStyledRow = rowFrame;
03712           break;
03713         }
03714         rowFrame = rowFrame->GetNextRow();
03715       }
03716     }
03717   }
03718 
03719   nsTableRowFrame* lastElligibleRow = nsnull;
03720   // accumulate the correct divisor. This will be the total of all unstyled rows inside 
03721   // unstyled row groups, unless there are none, in which case, it will be all rows
03722   nscoord divisor = 0;
03723   for (rgX = 0; rgX < numRowGroups; rgX++) {
03724     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
03725     if (rgFrame && (!firstUnStyledRG || !rgFrame->HasStyleHeight())) {
03726       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
03727       while (rowFrame) {
03728         if (!firstUnStyledRG || !rowFrame->HasStyleHeight()) {
03729           divisor += rowFrame->GetSize().height;
03730           lastElligibleRow = rowFrame;
03731         }
03732         rowFrame = rowFrame->GetNextRow();
03733       }
03734     }
03735   }
03736   if (divisor <= 0) {
03737     NS_ERROR("invalid divisor");
03738     return;
03739   }
03740 
03741   // allocate the extra height to the unstyled row groups and rows
03742   pctBasis = aAmount - amountUsed;
03743   yOriginRG = borderPadding.top + cellSpacingY;
03744   yEndRG = yOriginRG;
03745   for (rgX = 0; rgX < numRowGroups; rgX++) {
03746     nsTableRowGroupFrame* rgFrame = GetRowGroupFrame((nsIFrame*)rowGroups.ElementAt(rgX));
03747     if (!rgFrame) continue; 
03748     nscoord amountUsedByRG = 0;
03749     nscoord yOriginRow = 0;
03750     nsRect rgRect = rgFrame->GetRect();
03751     // see if there is an eligible row group
03752     if (!firstUnStyledRG || !rgFrame->HasStyleHeight()) {
03753       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
03754       while (rowFrame) {
03755         nsRect rowRect = rowFrame->GetRect();
03756         // see if there is an eligible row
03757         if (!firstUnStyledRow || !rowFrame->HasStyleHeight()) {
03758           // The amount of additional space each row gets is proportional to its height
03759           float percent = rowRect.height / ((float)divisor);
03760           // give rows their percentage, except for the last row which gets the remainder
03761           nscoord amountForRow = (rowFrame == lastElligibleRow) 
03762                                  ? aAmount - amountUsed : NSToCoordRound(((float)(pctBasis)) * percent);
03763           amountForRow = PR_MIN(nsTableFrame::RoundToPixel(amountForRow, p2t), aAmount - amountUsed);
03764           // update the row height
03765           nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width, rowRect.height + amountForRow);
03766           rowFrame->SetRect(newRowRect);
03767           yOriginRow += newRowRect.height + cellSpacingY;
03768           yEndRG += newRowRect.height + cellSpacingY;
03769 
03770           amountUsed += amountForRow;
03771           amountUsedByRG += amountForRow;
03772           NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
03773           //rowFrame->DidResize(aPresContext, aReflowState);        
03774           nsTableFrame::RePositionViews(rowFrame);
03775         }
03776         else {
03777           if (amountUsed > 0) {
03778             rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
03779             nsTableFrame::RePositionViews(rowFrame);
03780           }
03781           yOriginRow += rowRect.height + cellSpacingY;
03782           yEndRG += rowRect.height + cellSpacingY;
03783         }
03784         rowFrame = rowFrame->GetNextRow();
03785       }
03786       if (amountUsed > 0) {
03787         rgRect.y = yOriginRG;
03788         rgRect.height += amountUsedByRG;
03789         rgFrame->SetRect(rgRect);
03790       }
03791       // Make sure child views are properly positioned
03792       // XXX what happens if childFrame is a scroll frame and this gets skipped? see also below
03793     }
03794     else if (amountUsed > 0) {
03795       rgFrame->SetPosition(nsPoint(0, yOriginRG));
03796       // Make sure child views are properly positioned
03797       nsTableFrame::RePositionViews(rgFrame);
03798     }
03799     yOriginRG = yEndRG;
03800   }
03801 
03802   ResizeCells(*this, presContext, aReflowState);
03803 }
03804 
03805 static void
03806 UpdateCol(nsTableFrame&           aTableFrame,
03807           nsTableColFrame&        aColFrame,
03808           const nsTableCellFrame& aCellFrame,
03809           nscoord                 aColMaxWidth,
03810           PRBool                  aColMaxGetsBigger)
03811 {
03812   if (aColMaxGetsBigger) {
03813     // update the columns's new min width
03814     aColFrame.SetWidth(DES_CON, aColMaxWidth);
03815   }
03816   else {
03817     // determine the new max width
03818     PRInt32 numRows = aTableFrame.GetRowCount();
03819     PRInt32 colIndex = aColFrame.GetColIndex();
03820     PRBool originates;
03821     PRInt32 colSpan;
03822     nscoord maxWidth = 0;
03823     for (PRInt32 rowX = 0; rowX < numRows; rowX++) {
03824       nsTableCellFrame* cellFrame = aTableFrame.GetCellInfoAt(rowX, colIndex, &originates, &colSpan);
03825       if (cellFrame && originates && (1 == colSpan)) {
03826         maxWidth = PR_MAX(maxWidth, cellFrame->GetMaximumWidth());
03827       }
03828     }
03829     // update the columns's new max width
03830     aColFrame.SetWidth(DES_CON, maxWidth);
03831   }
03832 }
03833 
03834 PRBool 
03835 nsTableFrame::IsPctHeight(nsStyleContext* aStyleContext) 
03836 {
03837   PRBool result = PR_FALSE;
03838   if (aStyleContext) {
03839     result = (eStyleUnit_Percent ==
03840               aStyleContext->GetStylePosition()->mHeight.GetUnit());
03841   }
03842   return result;
03843 }
03844 
03845 PRBool 
03846 nsTableFrame::CellChangedWidth(const nsTableCellFrame& aCellFrame,
03847                                nscoord                 aPrevCellMin,
03848                                nscoord                 aPrevCellMax,
03849                                PRBool                  aCellWasDestroyed)
03850 {
03851   if (NeedStrategyInit() || !IsAutoLayout()) {
03852     // if the strategy needs to be initialized, all of the col info will be updated later
03853     // fixed layout tables do not cause any rebalancing
03854     return PR_TRUE;
03855   }
03856 
03857   nscoord colSpan = GetEffectiveColSpan(aCellFrame);
03858   if (colSpan > 1) {
03859     // colspans are too complicated to optimize, so just bail out
03860     SetNeedStrategyInit(PR_TRUE);
03861     return PR_TRUE;
03862   }
03863 
03864   PRInt32 rowX, colIndex, numRows;
03865   aCellFrame.GetColIndex(colIndex);
03866   
03867   PRBool originates;
03868 
03869   nsTableColFrame* colFrame = GetColFrame(colIndex);
03870   if (!colFrame) return PR_TRUE; // should never happen
03871 
03872   nscoord cellMin = (aCellWasDestroyed) ? 0 : aCellFrame.GetPass1MaxElementWidth();
03873   nscoord cellMax = (aCellWasDestroyed) ? 0 : aCellFrame.GetMaximumWidth();
03874   nscoord colMin  = colFrame->GetWidth(MIN_CON);
03875   nscoord colMax  = colFrame->GetWidth(DES_CON);
03876 
03877   PRBool colMinGetsBigger  = (cellMin > colMin);
03878   PRBool colMinGetsSmaller = (cellMin < colMin) && (colMin == aPrevCellMin);
03879 
03880   if (colMinGetsBigger || colMinGetsSmaller) {
03881     if (ColIsSpannedInto(colIndex) || ColHasSpanningCells(colIndex)) {
03882       // bail out if a colspan is involved
03883       SetNeedStrategyInit(PR_TRUE);
03884       return PR_TRUE;
03885     }
03886     if (colMinGetsBigger) {
03887       // update the columns's min width
03888       colFrame->SetWidth(MIN_CON, cellMin);
03889     }
03890     else if (colMinGetsSmaller) {
03891       // determine the new min width
03892       numRows = GetRowCount();
03893       nscoord minWidth = 0;
03894       for (rowX = 0; rowX < numRows; rowX++) {
03895         nsTableCellFrame* cellFrame = GetCellInfoAt(rowX, colIndex, &originates, &colSpan);
03896         if (cellFrame && originates && (1 == colSpan)) {
03897           minWidth = PR_MAX(minWidth, cellFrame->GetPass1MaxElementWidth());
03898         }
03899       }
03900       // update the columns's new min width
03901       colFrame->SetWidth(MIN_CON, minWidth);
03902     }
03903     // we should rebalance in case the min width determines the column width
03904     SetNeedStrategyBalance(PR_TRUE);
03905   }
03906 
03907   PRBool colMaxGetsBigger  = (cellMax > colMax);
03908   PRBool colMaxGetsSmaller = (cellMax < colMax) && (colMax == aPrevCellMax);
03909 
03910   if (colMaxGetsBigger || colMaxGetsSmaller) {
03911     if (ColIsSpannedInto(colIndex)) {
03912       // bail out if a colspan is involved
03913       SetNeedStrategyInit(PR_TRUE);
03914       return PR_TRUE;
03915     }
03916     // see if the max width will be not be overshadowed by a pct, fix, or proportional width
03917     if ((colFrame->GetWidth(PCT) <= 0) && (colFrame->GetWidth(FIX) <= 0) &&
03918         (colFrame->GetWidth(MIN_PRO) <= 0)) {
03919       // see if the doesn't have a pct width
03920       const nsStylePosition* cellPosition = aCellFrame.GetStylePosition();
03921       // see if there isn't a pct width on the cell
03922       PRBool havePct = PR_FALSE;
03923       if (eStyleUnit_Percent == cellPosition->mWidth.GetUnit()) {
03924         float percent = cellPosition->mWidth.GetPercentValue();
03925         if (percent > 0.0f) {
03926           havePct = PR_TRUE;
03927         }
03928       }
03929       if (!havePct) {
03930         // see if there isn't a fix width on the cell
03931         PRBool haveFix = PR_FALSE;
03932         if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit()) {
03933           nscoord coordValue = cellPosition->mWidth.GetCoordValue();
03934           if (coordValue > 0) { 
03935             haveFix = PR_TRUE;
03936           }
03937         }
03938         if (!haveFix) {
03939           // see if there isn't a prop width on the cell
03940           PRBool haveProp = PR_FALSE;
03941           if (eStyleUnit_Proportional == cellPosition->mWidth.GetUnit()) {
03942             nscoord intValue = cellPosition->mWidth.GetIntValue();
03943             if (intValue > 0) { 
03944               haveProp = PR_TRUE;
03945             }
03946           }
03947           if (!haveProp) {
03948             UpdateCol(*this, *colFrame, aCellFrame, cellMax, colMaxGetsBigger);
03949             // we should rebalance in case the max width determines the column width
03950             SetNeedStrategyBalance(PR_TRUE);
03951           }
03952         }
03953       }
03954     }
03955     else {
03956       UpdateCol(*this, *colFrame, aCellFrame, cellMax, colMaxGetsBigger);
03957     }
03958   }
03959   return PR_FALSE;
03960 }
03961 
03962 void nsTableFrame::SetNeedStrategyBalance(PRBool aValue)
03963 {
03964   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
03965   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
03966   firstInFlow->mBits.mNeedStrategyBalance = aValue;
03967 }
03968 
03969 PRBool nsTableFrame::NeedStrategyBalance() const
03970 {
03971   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
03972   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
03973   return (PRBool)firstInFlow->mBits.mNeedStrategyBalance;
03974 }
03975 
03976 void nsTableFrame::SetNeedStrategyInit(PRBool aValue)
03977 {
03978   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
03979   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
03980   firstInFlow->mBits.mNeedStrategyInit = aValue;
03981 }
03982 
03983 PRBool nsTableFrame::NeedStrategyInit() const
03984 {
03985   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
03986   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
03987   return (PRBool)firstInFlow->mBits.mNeedStrategyInit;
03988 }
03989 
03990 void nsTableFrame::SetResizeReflow(PRBool aValue)
03991 {
03992   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
03993   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
03994   firstInFlow->mBits.mDidResizeReflow = aValue;
03995 }
03996 
03997 PRBool nsTableFrame::DidResizeReflow() const
03998 {
03999   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
04000   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
04001   return (PRBool)firstInFlow->mBits.mDidResizeReflow;
04002 }
04003 
04004 PRInt32 nsTableFrame::GetColumnWidth(PRInt32 aColIndex)
04005 {
04006   nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
04007   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
04008   PRInt32 result = 0;
04009   if (this == firstInFlow) {
04010     nsTableColFrame* colFrame = GetColFrame(aColIndex);
04011     if (colFrame) {
04012       result = colFrame->GetWidth(FINAL);
04013     }
04014   }
04015   else {
04016     result = firstInFlow->GetColumnWidth(aColIndex);
04017   }
04018 
04019   return result;
04020 }
04021 
04022 void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth)
04023 {
04024   nsTableFrame* firstInFlow = (nsTableFrame *)GetFirstInFlow();
04025   NS_ASSERTION(firstInFlow, "illegal state -- no first in flow");
04026 
04027   if (this == firstInFlow) {
04028     nsTableColFrame* colFrame = GetColFrame(aColIndex);
04029     if (colFrame) {
04030       colFrame->SetWidth(FINAL, aWidth);
04031     }
04032     else {
04033       NS_ASSERTION(PR_FALSE, "null col frame");
04034     }
04035   }
04036   else {
04037     firstInFlow->SetColumnWidth(aColIndex, aWidth);
04038   }
04039 }
04040 
04041 
04042 nscoord 
04043 CalcPercentPadding(nscoord      aBasis,
04044                    nsStyleCoord aStyleCoord)
04045 {
04046   float percent = (NS_UNCONSTRAINEDSIZE == aBasis)
04047                   ? 0 : aStyleCoord.GetPercentValue();
04048   return NSToCoordRound(((float)aBasis) * percent);
04049 }
04050 
04051 void 
04052 GetPaddingFor(const nsSize&         aBasis, 
04053               const nsStylePadding& aPaddingData, 
04054               nsMargin&             aPadding)
04055 {
04056   nsStyleCoord styleCoord;
04057   aPaddingData.mPadding.GetTop(styleCoord);
04058   if (eStyleUnit_Percent == aPaddingData.mPadding.GetTopUnit()) {
04059     aPadding.top = CalcPercentPadding(aBasis.height, styleCoord);
04060   }
04061   else if (eStyleUnit_Coord == aPaddingData.mPadding.GetTopUnit()) {
04062     aPadding.top = styleCoord.GetCoordValue();
04063   }
04064 
04065   aPaddingData.mPadding.GetRight(styleCoord);
04066   if (eStyleUnit_Percent == aPaddingData.mPadding.GetRightUnit()) {
04067     aPadding.right = CalcPercentPadding(aBasis.width, styleCoord);
04068   }
04069   else if (eStyleUnit_Coord == aPaddingData.mPadding.GetTopUnit()) {
04070     aPadding.right = styleCoord.GetCoordValue();
04071   }
04072 
04073   aPaddingData.mPadding.GetBottom(styleCoord);
04074   if (eStyleUnit_Percent == aPaddingData.mPadding.GetBottomUnit()) {
04075     aPadding.bottom = CalcPercentPadding(aBasis.height, styleCoord);
04076   }
04077   else if (eStyleUnit_Coord == aPaddingData.mPadding.GetTopUnit()) {
04078     aPadding.bottom = styleCoord.GetCoordValue();
04079   }
04080 
04081   aPaddingData.mPadding.GetLeft(styleCoord);
04082   if (eStyleUnit_Percent == aPaddingData.mPadding.GetLeftUnit()) {
04083     aPadding.left = CalcPercentPadding(aBasis.width, styleCoord);
04084   }
04085   else if (eStyleUnit_Coord == aPaddingData.mPadding.GetTopUnit()) {
04086     aPadding.left = styleCoord.GetCoordValue();
04087   }
04088 }
04089 
04090 nsMargin
04091 nsTableFrame::GetBorderPadding(const nsHTMLReflowState& aReflowState,
04092                                float                    aPixelToTwips,
04093                                const nsTableCellFrame*  aCellFrame)
04094 {
04095   const nsStylePadding* paddingData = aCellFrame->GetStylePadding();
04096   nsMargin padding(0,0,0,0);
04097   if (!paddingData->GetPadding(padding)) {
04098     const nsHTMLReflowState* parentRS = aReflowState.parentReflowState;
04099     while (parentRS) {
04100       if (parentRS->frame) {
04101         if (nsLayoutAtoms::tableFrame == parentRS->frame->GetType()) {
04102           nsSize basis(parentRS->mComputedWidth, parentRS->mComputedHeight);
04103           GetPaddingFor(basis, *paddingData, padding);
04104           break;
04105         }
04106       }
04107       parentRS = parentRS->parentReflowState;
04108     }
04109   }
04110   nsMargin border;
04111   aCellFrame->GetBorderWidth(aPixelToTwips, border);
04112   padding += border;
04113   return padding;
04114 }
04115 
04116 nsMargin
04117 nsTableFrame::GetBorderPadding(const nsSize&           aBasis,
04118                                float                   aPixelToTwips,
04119                                const nsTableCellFrame* aCellFrame)
04120 {
04121   const nsStylePadding* paddingData = aCellFrame->GetStylePadding();
04122   nsMargin padding(0,0,0,0);
04123   if (!paddingData->GetPadding(padding)) {
04124     GetPaddingFor(aBasis, *paddingData, padding);
04125   }
04126   nsMargin border;
04127   aCellFrame->GetBorderWidth(aPixelToTwips, border);
04128   padding += border;
04129   return padding;
04130 }
04131 
04132 // XXX: could cache this.  But be sure to check style changes if you do!
04133 nscoord nsTableFrame::GetCellSpacingX()
04134 {
04135   if (IsBorderCollapse())
04136     return 0;
04137 
04138   NS_ASSERTION(GetStyleTableBorder()->mBorderSpacingX.GetUnit() == eStyleUnit_Coord,
04139                "Not a coord value!");
04140   return GetStyleTableBorder()->mBorderSpacingX.GetCoordValue();
04141 }
04142 
04143 // XXX: could cache this. But be sure to check style changes if you do!
04144 nscoord nsTableFrame::GetCellSpacingY()
04145 {
04146   if (IsBorderCollapse())
04147     return 0;
04148 
04149   NS_ASSERTION(GetStyleTableBorder()->mBorderSpacingY.GetUnit() == eStyleUnit_Coord,
04150                "Not a coord value!");
04151   return GetStyleTableBorder()->mBorderSpacingY.GetCoordValue();
04152 }
04153 
04154 /* ----- global methods ----- */
04155 
04156 nsresult 
04157 NS_NewTableFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
04158 {
04159   NS_PRECONDITION(aNewFrame, "null OUT ptr");
04160   if (nsnull == aNewFrame) {
04161     return NS_ERROR_NULL_POINTER;
04162   }
04163   nsTableFrame* it = new (aPresShell) nsTableFrame;
04164   if (nsnull == it) {
04165     return NS_ERROR_OUT_OF_MEMORY;
04166   }
04167   *aNewFrame = it;
04168   return NS_OK;
04169 }
04170 
04171 NS_METHOD 
04172 nsTableFrame::GetTableFrame(nsIFrame*      aSourceFrame, 
04173                             nsTableFrame*& aTableFrame)
04174 {
04175   nsresult rv = NS_ERROR_UNEXPECTED;  // the value returned
04176   aTableFrame = nsnull;               // initialize out-param
04177   if (aSourceFrame) {
04178     // "result" is the result of intermediate calls, not the result we return from this method
04179     for (nsIFrame* parentFrame = aSourceFrame->GetParent(); parentFrame;
04180          parentFrame = parentFrame->GetParent()) {
04181       if (nsLayoutAtoms::tableFrame == parentFrame->GetType()) {
04182         aTableFrame = (nsTableFrame*)parentFrame;
04183         rv = NS_OK; // only set if we found the table frame
04184         break;
04185       }
04186     }
04187   }
04188   NS_POSTCONDITION(nsnull!=aTableFrame, "unable to find table parent. aTableFrame null.");
04189   NS_POSTCONDITION(NS_OK==rv, "unable to find table parent. result!=NS_OK");
04190   return rv;
04191 }
04192 
04193 PRBool 
04194 nsTableFrame::IsAutoWidth(PRBool* aIsPctWidth)
04195 {
04196   return nsTableOuterFrame::IsAutoWidth(*this, aIsPctWidth);
04197 }
04198 
04199 PRBool 
04200 nsTableFrame::IsAutoHeight()
04201 {
04202   PRBool isAuto = PR_TRUE;  // the default
04203 
04204   const nsStylePosition* position = GetStylePosition();
04205 
04206   switch (position->mHeight.GetUnit()) {
04207     case eStyleUnit_Auto:         // specified auto width
04208     case eStyleUnit_Proportional: // illegal for table, so ignored
04209       break;
04210     case eStyleUnit_Coord:
04211       isAuto = PR_FALSE;
04212       break;
04213     case eStyleUnit_Percent:
04214       if (position->mHeight.GetPercentValue() > 0.0f) {
04215         isAuto = PR_FALSE;
04216       }
04217       break;
04218     default:
04219       break;
04220   }
04221 
04222   return isAuto; 
04223 }
04224 
04225 nscoord 
04226 nsTableFrame::CalcBorderBoxWidth(const nsHTMLReflowState& aState)
04227 {
04228   nscoord width = aState.mComputedWidth;
04229 
04230   if (eStyleUnit_Auto == aState.mStylePosition->mWidth.GetUnit()) {
04231     if (0 == width) {
04232       width = aState.availableWidth;
04233     }
04234     if (NS_UNCONSTRAINEDSIZE != aState.availableWidth) {
04235       width = aState.availableWidth;
04236     }
04237   }
04238   else if (width != NS_UNCONSTRAINEDSIZE) {
04239     nsMargin borderPadding = GetContentAreaOffset(&aState);
04240     width += borderPadding.left + borderPadding.right;
04241   }
04242   width = PR_MAX(width, 0);
04243 
04244   if (NS_UNCONSTRAINEDSIZE != width) {
04245     float p2t;
04246     p2t = GetPresContext()->PixelsToTwips();
04247     width = RoundToPixel(width, p2t, eRoundUpIfHalfOrMore);
04248   }
04249 
04250   return width;
04251 }
04252 
04253 nscoord 
04254 nsTableFrame::CalcBorderBoxHeight(const nsHTMLReflowState& aState)
04255 {
04256   nscoord height = aState.mComputedHeight;
04257   if (NS_AUTOHEIGHT != height) {
04258     nsMargin borderPadding = GetContentAreaOffset(&aState);
04259     height += borderPadding.top + borderPadding.bottom;
04260   }
04261   height = PR_MAX(0, height);
04262 
04263   return height;
04264 }
04265 
04266 nscoord 
04267 nsTableFrame::GetMinCaptionWidth()
04268 {
04269   return NS_STATIC_CAST(nsTableOuterFrame*, GetParent())->GetMinCaptionWidth();
04270 }
04271 
04272 PRBool 
04273 nsTableFrame::IsAutoLayout()
04274 {
04275   if (NS_STYLE_TABLE_LAYOUT_FIXED == GetStyleTable()->mLayoutStrategy) {
04276     // a fixed-layout table must have a width
04277     if (eStyleUnit_Auto != GetStylePosition()->mWidth.GetUnit()) {
04278       return PR_FALSE;
04279     }
04280   }
04281   return PR_TRUE;
04282 }
04283 
04284 #ifdef DEBUG
04285 NS_IMETHODIMP
04286 nsTableFrame::GetFrameName(nsAString& aResult) const
04287 {
04288   return MakeFrameName(NS_LITERAL_STRING("Table"), aResult);
04289 }
04290 #endif
04291 
04292 
04293 void 
04294 nsTableFrame::CalcMinAndPreferredWidths(const           nsHTMLReflowState& aReflowState,
04295                                         PRBool          aCalcPrefWidthIfAutoWithPctCol,
04296                                         nscoord&        aMinWidth,
04297                                         nscoord&        aPrefWidth) 
04298 {
04299   aMinWidth = aPrefWidth = 0;
04300 
04301   nscoord spacingX = GetCellSpacingX();
04302   PRInt32 numCols = GetColCount();
04303 
04304   for (PRInt32 colX = 0; colX < numCols; colX++) { 
04305     nsTableColFrame* colFrame = GetColFrame(colX);
04306     if (!colFrame) continue;
04307     aMinWidth += colFrame->GetMinWidth();
04308     nscoord width = colFrame->GetFixWidth();
04309     if (width <= 0) {
04310       width = colFrame->GetDesWidth();
04311     }
04312     aPrefWidth += width;
04313     if (GetNumCellsOriginatingInCol(colX) > 0) {
04314       aMinWidth  += spacingX;
04315       aPrefWidth += spacingX;
04316     }
04317   }
04318   // if it is not a degenerate table, add the last spacing on the right and the borderPadding
04319   if (numCols > 0) {
04320     nsMargin childAreaOffset = GetChildAreaOffset( &aReflowState);
04321     nscoord extra = spacingX + childAreaOffset.left + childAreaOffset.right;
04322     aMinWidth  += extra;
04323     aPrefWidth += extra;
04324   }
04325   aPrefWidth = PR_MAX(aMinWidth, aPrefWidth);
04326 
04327   PRBool isPctWidth = PR_FALSE;
04328   if (IsAutoWidth(&isPctWidth)) {
04329     if (HasPctCol() && aCalcPrefWidthIfAutoWithPctCol && 
04330         (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth)) {
04331       // for an auto table with a pct cell, use the strategy's CalcPctAdjTableWidth
04332       nscoord availWidth = CalcBorderBoxWidth(aReflowState);
04333       availWidth = PR_MIN(availWidth, aReflowState.availableWidth);
04334       if (mTableLayoutStrategy && IsAutoLayout()) {
04335         aPrefWidth = mTableLayoutStrategy->CalcPctAdjTableWidth(aReflowState, availWidth);
04336       }
04337     }
04338     if (0 == numCols) { // degenerate case
04339       aMinWidth = aPrefWidth = 0;
04340     }
04341   }
04342   else { // a specified fix width becomes the min or preferred width
04343     nscoord compWidth = aReflowState.mComputedWidth;
04344     if ((NS_UNCONSTRAINEDSIZE != compWidth) && (0 != compWidth) && !isPctWidth) {
04345       nsMargin contentOffset = GetContentAreaOffset(&aReflowState);
04346       compWidth += contentOffset.left + contentOffset.right;
04347       aMinWidth = PR_MAX(aMinWidth, compWidth);
04348       aPrefWidth = PR_MAX(aMinWidth, compWidth);
04349     }
04350   }
04351 }
04352 
04353 
04354 // Find the closet sibling before aPriorChildFrame (including aPriorChildFrame) that
04355 // is of type aChildType
04356 nsIFrame* 
04357 nsTableFrame::GetFrameAtOrBefore(nsIFrame*       aParentFrame,
04358                                  nsIFrame*       aPriorChildFrame,
04359                                  nsIAtom*        aChildType)
04360 {
04361   nsIFrame* result = nsnull;
04362   if (!aPriorChildFrame) {
04363     return result;
04364   }
04365   if (aChildType == aPriorChildFrame->GetType()) {
04366     return aPriorChildFrame;
04367   }
04368 
04369   // aPriorChildFrame is not of type aChildType, so we need start from 
04370   // the beginnng and find the closest one 
04371   nsIFrame* lastMatchingFrame = nsnull;
04372   nsIFrame* childFrame = aParentFrame->GetFirstChild(nsnull);
04373   while (childFrame && (childFrame != aPriorChildFrame)) {
04374     if (aChildType == childFrame->GetType()) {
04375       lastMatchingFrame = childFrame;
04376     }
04377     childFrame = childFrame->GetNextSibling();
04378   }
04379   return lastMatchingFrame;
04380 }
04381 
04382 #ifdef DEBUG
04383 void 
04384 nsTableFrame::DumpRowGroup(nsIFrame* aKidFrame)
04385 {
04386   nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(aKidFrame);
04387   if (rgFrame) {
04388     nsIFrame* rowFrame = rgFrame->GetFirstChild(nsnull);
04389     while (rowFrame) {
04390       if (nsLayoutAtoms::tableRowFrame == rowFrame->GetType()) {
04391         printf("row(%d)=%p ", ((nsTableRowFrame*)rowFrame)->GetRowIndex(), rowFrame);
04392         nsIFrame* cellFrame = rowFrame->GetFirstChild(nsnull);
04393         while (cellFrame) {
04394           if (IS_TABLE_CELL(cellFrame->GetType())) {
04395             PRInt32 colIndex;
04396             ((nsTableCellFrame*)cellFrame)->GetColIndex(colIndex);
04397             printf("cell(%d)=%p ", colIndex, cellFrame);
04398           }
04399           cellFrame = cellFrame->GetNextSibling();
04400         }
04401         printf("\n");
04402       }
04403       else {
04404         DumpRowGroup(rowFrame);
04405       }
04406       rowFrame = rowFrame->GetNextSibling();
04407     }
04408   }
04409 }
04410 
04411 void 
04412 nsTableFrame::Dump(PRBool          aDumpRows,
04413                    PRBool          aDumpCols, 
04414                    PRBool          aDumpCellMap)
04415 {
04416   printf("***START TABLE DUMP*** \n");
04417   // dump the columns widths array
04418   printf("mColWidths=");
04419   PRInt32 numCols = GetColCount();
04420   PRInt32 colX;
04421   for (colX = 0; colX < numCols; colX++) {
04422     printf("%d ", GetColumnWidth(colX));
04423   }
04424   printf("\n");
04425 
04426   if (aDumpRows) {
04427     nsIFrame* kidFrame = mFrames.FirstChild();
04428     while (kidFrame) {
04429       DumpRowGroup(kidFrame);
04430       kidFrame = kidFrame->GetNextSibling();
04431     }
04432   }
04433 
04434   if (aDumpCols) {
04435          // output col frame cache
04436     printf("\n col frame cache ->");
04437           for (colX = 0; colX < numCols; colX++) {
04438       nsTableColFrame* colFrame = (nsTableColFrame *)mColFrames.ElementAt(colX);
04439       if (0 == (colX % 8)) {
04440         printf("\n");
04441       }
04442       printf ("%d=%p ", colX, colFrame);
04443       nsTableColType colType = colFrame->GetColType();
04444       switch (colType) {
04445       case eColContent:
04446         printf(" content ");
04447         break;
04448       case eColAnonymousCol: 
04449         printf(" anonymous-column ");
04450         break;
04451       case eColAnonymousColGroup:
04452         printf(" anonymous-colgroup ");
04453         break;
04454       case eColAnonymousCell: 
04455         printf(" anonymous-cell ");
04456         break;
04457       }
04458     }
04459     printf("\n colgroups->");
04460     for (nsIFrame* childFrame = mColGroups.FirstChild(); childFrame;
04461          childFrame = childFrame->GetNextSibling()) {
04462       if (nsLayoutAtoms::tableColGroupFrame == childFrame->GetType()) {
04463         nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame *)childFrame;
04464         colGroupFrame->Dump(1);
04465       }
04466     }
04467     for (colX = 0; colX < numCols; colX++) {
04468       printf("\n");
04469       nsTableColFrame* colFrame = GetColFrame(colX);
04470       colFrame->Dump(1);
04471     }
04472   }
04473   if (aDumpCellMap) {
04474     nsTableCellMap* cellMap = GetCellMap();
04475     cellMap->Dump();
04476   }
04477   printf(" ***END TABLE DUMP*** \n");
04478 }
04479 #endif
04480 
04481 // nsTableIterator
04482 nsTableIterator::nsTableIterator(nsIFrame&        aSource,
04483                                  nsTableIteration aType)
04484 {
04485   nsIFrame* firstChild = aSource.GetFirstChild(nsnull);
04486   Init(firstChild, aType);
04487 }
04488 
04489 nsTableIterator::nsTableIterator(nsFrameList&     aSource,
04490                                  nsTableIteration aType)
04491 {
04492   nsIFrame* firstChild = aSource.FirstChild();
04493   Init(firstChild, aType);
04494 }
04495 
04496 void nsTableIterator::Init(nsIFrame*        aFirstChild,
04497                            nsTableIteration aType)
04498 {
04499   mFirstListChild = aFirstChild;
04500   mFirstChild     = aFirstChild;
04501   mCurrentChild   = nsnull;
04502   mLeftToRight    = (eTableRTL == aType) ? PR_FALSE : PR_TRUE; 
04503   mCount          = -1;
04504 
04505   if (!mFirstChild) {
04506     return;
04507   }
04508   if (eTableDIR == aType) {
04509     nsTableFrame* table = nsnull;
04510     nsresult rv = nsTableFrame::GetTableFrame(mFirstChild, table);
04511     if (NS_SUCCEEDED(rv) && (table != nsnull)) {
04512       mLeftToRight = (NS_STYLE_DIRECTION_LTR ==
04513                       table->GetStyleVisibility()->mDirection);
04514     }
04515     else {
04516       NS_ASSERTION(PR_FALSE, "source of table iterator is not part of a table");
04517       return;
04518     }
04519   }
04520   if (!mLeftToRight) {
04521     mCount = 0;
04522     nsIFrame* nextChild = mFirstChild->GetNextSibling();
04523     while (nsnull != nextChild) {
04524       mCount++;
04525       mFirstChild = nextChild;
04526       nextChild = nextChild->GetNextSibling();
04527     }
04528   } 
04529 }
04530 
04531 nsIFrame* nsTableIterator::First()
04532 {
04533   mCurrentChild = mFirstChild;
04534   return mCurrentChild;
04535 }
04536       
04537 nsIFrame* nsTableIterator::Next()
04538 {
04539   if (!mCurrentChild) {
04540     return nsnull;
04541   }
04542 
04543   if (mLeftToRight) {
04544     mCurrentChild = mCurrentChild->GetNextSibling();
04545     return mCurrentChild;
04546   }
04547   else {
04548     nsIFrame* targetChild = mCurrentChild;
04549     mCurrentChild = nsnull;
04550     nsIFrame* child = mFirstListChild;
04551     while (child && (child != targetChild)) {
04552       mCurrentChild = child;
04553       child = child->GetNextSibling();
04554     }
04555     return mCurrentChild;
04556   }
04557 }
04558 
04559 PRBool nsTableIterator::IsLeftToRight()
04560 {
04561   return mLeftToRight;
04562 }
04563 
04564 PRInt32 nsTableIterator::Count()
04565 {
04566   if (-1 == mCount) {
04567     mCount = 0;
04568     nsIFrame* child = mFirstListChild;
04569     while (nsnull != child) {
04570       mCount++;
04571       child = child->GetNextSibling();
04572     }
04573   }
04574   return mCount;
04575 }
04576 
04577 nsTableCellFrame* nsTableFrame::GetCellInfoAt(PRInt32            aRowX, 
04578                                               PRInt32            aColX, 
04579                                               PRBool*            aOriginates, 
04580                                               PRInt32*           aColSpan)
04581 {
04582   nsTableCellMap* cellMap = GetCellMap();
04583   return cellMap->GetCellInfoAt(aRowX, aColX, aOriginates, aColSpan);
04584 }
04585 
04586 /*------------------ nsITableLayout methods ------------------------------*/
04587 NS_IMETHODIMP 
04588 nsTableFrame::GetCellDataAt(PRInt32        aRowIndex, 
04589                             PRInt32        aColIndex,
04590                             nsIDOMElement* &aCell,   //out params
04591                             PRInt32&       aStartRowIndex, 
04592                             PRInt32&       aStartColIndex, 
04593                             PRInt32&       aRowSpan, 
04594                             PRInt32&       aColSpan,
04595                             PRInt32&       aActualRowSpan, 
04596                             PRInt32&       aActualColSpan,
04597                             PRBool&        aIsSelected)
04598 {
04599   // Initialize out params
04600   aCell = nsnull;
04601   aStartRowIndex = 0;
04602   aStartColIndex = 0;
04603   aRowSpan = 0;
04604   aColSpan = 0;
04605   aIsSelected = PR_FALSE;
04606 
04607   nsTableCellMap* cellMap = GetCellMap();
04608   if (!cellMap) { return NS_ERROR_NOT_INITIALIZED;}
04609 
04610   PRBool originates;
04611   PRInt32 colSpan; // Is this the "effective" or "html" value?
04612 
04613   nsTableCellFrame *cellFrame = cellMap->GetCellInfoAt(aRowIndex, aColIndex, &originates, &colSpan);
04614   if (!cellFrame) return NS_TABLELAYOUT_CELL_NOT_FOUND;
04615 
04616   nsresult result= cellFrame->GetRowIndex(aStartRowIndex);
04617   if (NS_FAILED(result)) return result;
04618   result = cellFrame->GetColIndex(aStartColIndex);
04619   if (NS_FAILED(result)) return result;
04620   //This returns HTML value, which may be 0
04621   aRowSpan = cellFrame->GetRowSpan();
04622   aColSpan = cellFrame->GetColSpan();
04623   aActualRowSpan = GetEffectiveRowSpan(*cellFrame);
04624   aActualColSpan = GetEffectiveColSpan(*cellFrame);
04625 
04626   // If these aren't at least 1, we have a cellmap error
04627   if (aActualRowSpan == 0 || aActualColSpan == 0)
04628     return NS_ERROR_FAILURE;
04629 
04630   result = cellFrame->GetSelected(&aIsSelected);
04631   if (NS_FAILED(result)) return result;
04632 
04633   // do this last, because it addrefs, 
04634   // and we don't want the caller leaking it on error
04635   nsIContent* content = cellFrame->GetContent();
04636   if (!content) return NS_ERROR_FAILURE;   
04637   
04638   return CallQueryInterface(content, &aCell);                                      
04639 }
04640 
04641 NS_IMETHODIMP nsTableFrame::GetTableSize(PRInt32& aRowCount, PRInt32& aColCount)
04642 {
04643   nsTableCellMap* cellMap = GetCellMap();
04644   // Initialize out params
04645   aRowCount = 0;
04646   aColCount = 0;
04647   if (!cellMap) { return NS_ERROR_NOT_INITIALIZED;}
04648 
04649   aRowCount = cellMap->GetRowCount();
04650   aColCount = cellMap->GetColCount();
04651   return NS_OK;
04652 }
04653 
04654 /*---------------- end of nsITableLayout implementation ------------------*/
04655 
04656 PRInt32 nsTableFrame::GetNumCellsOriginatingInCol(PRInt32 aColIndex) const
04657 {
04658   nsTableCellMap* cellMap = GetCellMap();
04659   if (cellMap) 
04660     return cellMap->GetNumCellsOriginatingInCol(aColIndex);
04661   else
04662     return 0;
04663 }
04664 
04665 PRInt32 nsTableFrame::GetNumCellsOriginatingInRow(PRInt32 aRowIndex) const
04666 {
04667   nsTableCellMap* cellMap = GetCellMap();
04668   if (cellMap) 
04669     return cellMap->GetNumCellsOriginatingInRow(aRowIndex);
04670   else
04671     return 0;
04672 }
04673 
04674 static void
04675 CheckFixDamageArea(PRInt32 aNumRows,
04676                    PRInt32 aNumCols,
04677                    nsRect& aDamageArea)
04678 {
04679   if (((aDamageArea.XMost() > aNumCols) && (aDamageArea.width  != 1) && (aNumCols != 0)) || 
04680       ((aDamageArea.YMost() > aNumRows) && (aDamageArea.height != 1) && (aNumRows != 0))) {
04681     // the damage area was set incorrectly, just be safe and make it the entire table
04682     NS_ASSERTION(PR_FALSE, "invalid BC damage area");
04683     aDamageArea.x      = 0;
04684     aDamageArea.y      = 0;
04685     aDamageArea.width  = aNumCols;
04686     aDamageArea.height = aNumRows;
04687   }
04688 }
04689 
04690 /********************************************************************************
04691  * Collapsing Borders
04692  *
04693  *  The CSS spec says to resolve border conflicts in this order:
04694  *  1) any border with the style HIDDEN wins
04695  *  2) the widest border with a style that is not NONE wins
04696  *  3) the border styles are ranked in this order, highest to lowest precedence: 
04697  *     double, solid, dashed, dotted, ridge, outset, groove, inset
04698  *  4) borders that are of equal width and style (differ only in color) have this precedence:
04699  *     cell, row, rowgroup, col, colgroup, table
04700  *  5) if all border styles are NONE, then that's the computed border style.
04701  *******************************************************************************/
04702 
04703 void 
04704 nsTableFrame::SetBCDamageArea(const nsRect& aValue)
04705 {
04706   nsRect newRect(aValue);
04707   newRect.width  = PR_MAX(1, newRect.width);
04708   newRect.height = PR_MAX(1, newRect.height);
04709 
04710   if (!IsBorderCollapse()) {
04711     NS_ASSERTION(PR_FALSE, "invalid call - not border collapse model");
04712     return;
04713   }
04714   SetNeedToCalcBCBorders(PR_TRUE);
04715   // Get the property 
04716   BCPropertyData* value = (BCPropertyData*)nsTableFrame::GetProperty(this, nsLayoutAtoms::tableBCProperty, PR_TRUE);
04717   if (value) {
04718     // for now just construct a union of the new and old damage areas
04719     value->mDamageArea.UnionRect(value->mDamageArea, newRect);
04720     CheckFixDamageArea(GetRowCount(), GetColCount(), value->mDamageArea);
04721   }
04722 }
04723 /*****************************************************************
04724  *  BCMapCellIterator
04725  ****************************************************************/
04726 struct BCMapCellInfo 
04727 {
04728   BCMapCellInfo();
04729   void Reset();
04730 
04731   CellData*             cellData;
04732   nsCellMap*            cellMap;
04733 
04734   nsTableRowGroupFrame* rg;
04735 
04736   nsTableRowFrame*      topRow;
04737   nsTableRowFrame*      bottomRow;
04738 
04739   nsTableColGroupFrame* cg;
04740  
04741   nsTableColFrame*      leftCol;
04742   nsTableColFrame*      rightCol;
04743 
04744   nsBCTableCellFrame*   cell;
04745 
04746   PRInt32               rowIndex;
04747   PRInt32               rowSpan;
04748   PRInt32               colIndex;
04749   PRInt32               colSpan;
04750 
04751   PRPackedBool          rgTop;
04752   PRPackedBool          rgBottom;
04753   PRPackedBool          cgLeft;
04754   PRPackedBool          cgRight;
04755 };
04756 
04757 BCMapCellInfo::BCMapCellInfo()
04758 {
04759   Reset();
04760 }
04761 
04762 void BCMapCellInfo::Reset()
04763 {
04764   cellData  = nsnull;
04765   rg        = nsnull;
04766   topRow    = nsnull;
04767   bottomRow = nsnull;
04768   cg        = nsnull;
04769   leftCol   = nsnull;
04770   rightCol  = nsnull;
04771   cell      = nsnull;
04772   rowIndex = rowSpan = colIndex = colSpan = 0;
04773   rgTop = rgBottom = cgLeft = cgRight = PR_FALSE;
04774 }
04775 
04776 class BCMapCellIterator
04777 {
04778 public:
04779   BCMapCellIterator(nsTableFrame& aTableFrame,
04780                   const nsRect& aDamageArea);
04781 
04782   void First(BCMapCellInfo& aMapCellInfo);
04783 
04784   void Next(BCMapCellInfo& aMapCellInfo);
04785 
04786   void PeekRight(BCMapCellInfo& aRefInfo,
04787                  PRUint32     aRowIndex,
04788                  BCMapCellInfo& aAjaInfo);
04789 
04790   void PeekBottom(BCMapCellInfo& aRefInfo,
04791                   PRUint32     aColIndex,
04792                   BCMapCellInfo& aAjaInfo);
04793 
04794   PRBool IsNewRow() { return mIsNewRow; }
04795 
04796   nsTableRowFrame* GetPrevRow() const { return mPrevRow; }
04797 
04798   PRInt32    mRowGroupStart;
04799   PRInt32    mRowGroupEnd;
04800   PRBool     mAtEnd;
04801   nsCellMap* mCellMap;
04802 
04803 private:
04804   void SetInfo(nsTableRowFrame* aRow,
04805                PRInt32          aColIndex,
04806                CellData*        aCellData,
04807                BCMapCellInfo&   aMapInfo,
04808                nsCellMap*       aCellMap = nsnull);
04809 
04810   PRBool SetNewRow(nsTableRowFrame* row = nsnull);
04811   PRBool SetNewRowGroup(PRBool aFindFirstDamagedRow);
04812 
04813   nsTableFrame&         mTableFrame;
04814   nsTableCellMap*       mTableCellMap;
04815   nsVoidArray           mRowGroups;
04816   nsTableRowGroupFrame* mRowGroup;
04817   PRInt32               mRowGroupIndex;
04818   PRUint32              mNumRows;
04819   nsTableRowFrame*      mRow;
04820   nsTableRowFrame*      mPrevRow;
04821   PRBool                mIsNewRow;
04822   PRInt32               mRowIndex;
04823   PRUint32              mNumCols;
04824   PRInt32               mColIndex;
04825   nsPoint               mAreaStart;
04826   nsPoint               mAreaEnd;
04827 };
04828 
04829 BCMapCellIterator::BCMapCellIterator(nsTableFrame& aTableFrame,
04830                                      const nsRect& aDamageArea)
04831 :mTableFrame(aTableFrame)
04832 {
04833   mTableCellMap  = aTableFrame.GetCellMap();
04834 
04835   mAreaStart.x   = aDamageArea.x;
04836   mAreaStart.y   = aDamageArea.y;
04837   mAreaEnd.y     = aDamageArea.y + aDamageArea.height - 1;
04838   mAreaEnd.x     = aDamageArea.x + aDamageArea.width - 1;
04839 
04840   mNumRows       = mTableFrame.GetRowCount();
04841   mRow           = nsnull;
04842   mRowIndex      = 0;
04843   mNumCols       = mTableFrame.GetColCount();
04844   mColIndex      = 0;
04845   mRowGroupIndex = -1;
04846 
04847   // Get the ordered row groups 
04848   PRUint32 numRowGroups;
04849   aTableFrame.OrderRowGroups(mRowGroups, numRowGroups, nsnull);
04850 
04851   mAtEnd = PR_TRUE; // gets reset when First() is called
04852 }
04853 
04854 void 
04855 BCMapCellIterator::SetInfo(nsTableRowFrame* aRow,
04856                            PRInt32          aColIndex,
04857                            CellData*        aCellData,
04858                            BCMapCellInfo&   aCellInfo,
04859                            nsCellMap*       aCellMap)
04860 {
04861   aCellInfo.cellData = aCellData;
04862   aCellInfo.cellMap = (aCellMap) ? aCellMap : mCellMap;
04863   aCellInfo.colIndex = aColIndex;
04864 
04865   // row frame info
04866   aCellInfo.rowIndex = 0;
04867   if (aRow) {
04868     aCellInfo.topRow = aRow; 
04869     aCellInfo.rowIndex = aRow->GetRowIndex();
04870   }
04871 
04872   // cell frame info
04873   aCellInfo.cell      = nsnull;
04874   aCellInfo.rowSpan   = 1;
04875   aCellInfo.colSpan  = 1;
04876   if (aCellData) {
04877     aCellInfo.cell = (nsBCTableCellFrame*)aCellData->GetCellFrame(); 
04878     if (aCellInfo.cell) {
04879       if (!aCellInfo.topRow) {
04880         aCellInfo.topRow = NS_STATIC_CAST(nsTableRowFrame*,
04881                                           aCellInfo.cell->GetParent());
04882         if (!aCellInfo.topRow) ABORT0();
04883         aCellInfo.rowIndex = aCellInfo.topRow->GetRowIndex();
04884       }
04885       aCellInfo.colSpan = mTableFrame.GetEffectiveColSpan(*aCellInfo.cell, aCellMap); 
04886       aCellInfo.rowSpan = mTableFrame.GetEffectiveRowSpan(*aCellInfo.cell, aCellMap);
04887     }
04888   }
04889   if (!aCellInfo.topRow) {
04890     aCellInfo.topRow = mRow;
04891   }
04892 
04893   if (1 == aCellInfo.rowSpan) {
04894     aCellInfo.bottomRow = aCellInfo.topRow;
04895   }
04896   else {
04897     aCellInfo.bottomRow = aCellInfo.topRow->GetNextRow();
04898     if (aCellInfo.bottomRow) {
04899       for (PRInt32 spanX = 2; aCellInfo.bottomRow && (spanX < aCellInfo.rowSpan); spanX++) {
04900         aCellInfo.bottomRow = aCellInfo.bottomRow->GetNextRow();
04901       }
04902       NS_ASSERTION(aCellInfo.bottomRow, "program error");
04903     }
04904     else {
04905       NS_ASSERTION(PR_FALSE, "error in cell map");
04906       aCellInfo.rowSpan = 1;
04907       aCellInfo.bottomRow = aCellInfo.topRow;
04908     }
04909   }
04910 
04911   // row group frame info
04912   PRUint32 rgStart  = mRowGroupStart;
04913   PRUint32 rgEnd    = mRowGroupEnd;
04914   aCellInfo.rg = mTableFrame.GetRowGroupFrame(aCellInfo.topRow->GetParent());
04915   if (aCellInfo.rg != mRowGroup) {
04916     rgStart = aCellInfo.rg->GetStartRowIndex();
04917     rgEnd   = rgStart + aCellInfo.rg->GetRowCount() - 1;
04918   }
04919   PRUint32 rowIndex  = aCellInfo.topRow->GetRowIndex();
04920   aCellInfo.rgTop    = (rgStart == rowIndex);
04921   aCellInfo.rgBottom = (rgEnd == rowIndex + aCellInfo.rowSpan - 1);
04922 
04923   // col frame info
04924   aCellInfo.leftCol = mTableFrame.GetColFrame(aColIndex); if (!aCellInfo.leftCol) ABORT0();
04925 
04926   aCellInfo.rightCol = aCellInfo.leftCol;
04927   if (aCellInfo.colSpan > 1) {
04928     for (PRInt32 spanX = 1; spanX < aCellInfo.colSpan; spanX++) {
04929       nsTableColFrame* colFrame = mTableFrame.GetColFrame(aColIndex + spanX); if (!colFrame) ABORT0();
04930       aCellInfo.rightCol = colFrame;
04931     }
04932   }
04933 
04934   // col group frame info
04935   aCellInfo.cg = NS_STATIC_CAST(nsTableColGroupFrame*,
04936                                 aCellInfo.leftCol->GetParent());
04937   PRInt32 cgStart  = aCellInfo.cg->GetStartColumnIndex();
04938   PRInt32 cgEnd    = PR_MAX(0, cgStart + aCellInfo.cg->GetColCount() - 1);
04939   aCellInfo.cgLeft  = (cgStart == aColIndex);
04940   aCellInfo.cgRight = (cgEnd == aColIndex + (PRInt32)aCellInfo.colSpan - 1);
04941 }
04942 
04943 PRBool
04944 BCMapCellIterator::SetNewRow(nsTableRowFrame* aRow)
04945 {
04946   mAtEnd   = PR_TRUE;
04947   mPrevRow = mRow;
04948   if (aRow) {
04949     mRow = aRow;
04950   }
04951   else if (mRow) {
04952     mRow = mRow->GetNextRow();
04953   }
04954   if (mRow) {
04955     mRowIndex = mRow->GetRowIndex();
04956     // get to the first entry with an originating cell
04957     PRInt32 rgRowIndex = mRowIndex - mRowGroupStart;
04958     nsVoidArray* row = (nsVoidArray*)mCellMap->mRows.ElementAt(rgRowIndex); if (!row) ABORT1(PR_FALSE);
04959     PRInt32 rowSize = row->Count();
04960     for (mColIndex = mAreaStart.x; mColIndex <= mAreaEnd.x; mColIndex++) {
04961       CellData* cellData = (mColIndex < rowSize) ? (CellData*)row->ElementAt(mColIndex) : nsnull;
04962       if (!cellData) { // add a dead cell data
04963         nsRect damageArea;
04964         cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT1(PR_FALSE);
04965       }
04966       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
04967         break;
04968       }
04969     }
04970     mIsNewRow = PR_TRUE;
04971     mAtEnd    = PR_FALSE;
04972   }
04973   else ABORT1(PR_FALSE);
04974 
04975   return !mAtEnd;
04976 }
04977 
04978 PRBool
04979 BCMapCellIterator::SetNewRowGroup(PRBool aFindFirstDamagedRow)
04980 {
04981   mAtEnd = PR_TRUE;
04982   mRowGroupIndex++;
04983   PRInt32 numRowGroups = mRowGroups.Count();
04984   for (PRInt32 rgX = mRowGroupIndex; rgX < numRowGroups; rgX++) {
04985     nsIFrame* frame = (nsTableRowGroupFrame*)mRowGroups.ElementAt(mRowGroupIndex); if (!frame) ABORT1(PR_FALSE);
04986     mRowGroup = mTableFrame.GetRowGroupFrame(frame); if (!mRowGroup) ABORT1(PR_FALSE);
04987     PRInt32 rowCount = mRowGroup->GetRowCount();
04988     mRowGroupStart = mRowGroup->GetStartRowIndex();
04989     mRowGroupEnd   = mRowGroupStart + rowCount - 1;
04990     if (rowCount > 0) {
04991       mCellMap = mTableCellMap->GetMapFor(*mRowGroup); if (!mCellMap) ABORT1(PR_FALSE);
04992       nsTableRowFrame* firstRow = mRowGroup->GetFirstRow();
04993       if (aFindFirstDamagedRow) {
04994         if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
04995           // the damage area starts in the row group 
04996           if (aFindFirstDamagedRow) {
04997             // find the correct first damaged row
04998             PRInt32 numRows = mAreaStart.y - mRowGroupStart;
04999             for (PRInt32 i = 0; i < numRows; i++) {
05000               firstRow = firstRow->GetNextRow(); if (!frame) ABORT1(PR_FALSE);
05001             }
05002           }
05003         }
05004         else {
05005           mRowGroupIndex++;
05006           continue;
05007         }
05008       }
05009       if (SetNewRow(firstRow)) { // sets mAtEnd
05010         break;
05011       }
05012     }
05013   }
05014     
05015   return !mAtEnd;
05016 }
05017 
05018 void 
05019 BCMapCellIterator::First(BCMapCellInfo& aMapInfo)
05020 {
05021   aMapInfo.Reset();
05022 
05023   SetNewRowGroup(PR_TRUE); // sets mAtEnd
05024   while (!mAtEnd) {
05025     if ((mAreaStart.y >= mRowGroupStart) && (mAreaStart.y <= mRowGroupEnd)) {
05026       CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, mAreaStart.y - mRowGroupStart, mAreaStart.x, PR_FALSE);
05027       if (cellData && cellData->IsOrig()) {
05028         SetInfo(mRow, mAreaStart.x, cellData, aMapInfo);
05029       }
05030       else {
05031         NS_ASSERTION(((0 == mAreaStart.x) && (mRowGroupStart == mAreaStart.y)) , "damage area expanded incorrectly");
05032         mAtEnd = PR_TRUE;
05033       }
05034       break;
05035     }
05036     SetNewRowGroup(PR_TRUE); // sets mAtEnd
05037   } 
05038 }
05039 
05040 void 
05041 BCMapCellIterator::Next(BCMapCellInfo& aMapInfo)
05042 {
05043   if (mAtEnd) ABORT0();
05044   aMapInfo.Reset();
05045 
05046   mIsNewRow = PR_FALSE;
05047   mColIndex++;
05048   while ((mRowIndex <= mAreaEnd.y) && !mAtEnd) {
05049     for (; mColIndex <= mAreaEnd.x; mColIndex++) {
05050       PRInt32 rgRowIndex = mRowIndex - mRowGroupStart;
05051       CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, mColIndex, PR_TRUE);
05052       if (!cellData) { // add a dead cell data
05053         nsRect damageArea;
05054         cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0();
05055       }
05056       if (cellData && (cellData->IsOrig() || cellData->IsDead())) {
05057         SetInfo(mRow, mColIndex, cellData, aMapInfo);
05058         return;
05059       }
05060     }
05061     if (mRowIndex >= mRowGroupEnd) {
05062       SetNewRowGroup(PR_FALSE); // could set mAtEnd
05063     }
05064     else {
05065       SetNewRow(); // could set mAtEnd
05066     }
05067   }
05068   mAtEnd = PR_TRUE;
05069 }
05070 
05071 void 
05072 BCMapCellIterator::PeekRight(BCMapCellInfo&   aRefInfo,
05073                              PRUint32         aRowIndex,
05074                              BCMapCellInfo&   aAjaInfo)
05075 {
05076   aAjaInfo.Reset();
05077   PRInt32 colIndex = aRefInfo.colIndex + aRefInfo.colSpan;
05078   PRUint32 rgRowIndex = aRowIndex - mRowGroupStart;
05079 
05080   CellData* cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, colIndex, PR_TRUE);
05081   if (!cellData) { // add a dead cell data
05082     NS_ASSERTION(colIndex < mTableCellMap->GetColCount(), "program error");
05083     nsRect damageArea;
05084     cellData = mCellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0();
05085   }
05086   nsTableRowFrame* row = nsnull;
05087   if (cellData->IsRowSpan()) {
05088     rgRowIndex -= cellData->GetRowSpanOffset();
05089     cellData = mCellMap->GetDataAt(*mTableCellMap, rgRowIndex, colIndex, PR_FALSE); if (!cellData) ABORT0();
05090   }
05091   else {
05092     row = mRow;
05093   }
05094   SetInfo(row, colIndex, cellData, aAjaInfo);
05095 }
05096 
05097 void 
05098 BCMapCellIterator::PeekBottom(BCMapCellInfo&   aRefInfo,
05099                               PRUint32         aColIndex,
05100                               BCMapCellInfo&   aAjaInfo)
05101 {
05102   aAjaInfo.Reset();
05103   PRInt32 rowIndex = aRefInfo.rowIndex + aRefInfo.rowSpan;
05104   PRInt32 rgRowIndex = rowIndex - mRowGroupStart;
05105   nsTableRowGroupFrame* rg = mRowGroup;
05106   nsCellMap* cellMap = mCellMap;
05107   nsTableRowFrame* nextRow = nsnull;
05108   if (rowIndex > mRowGroupEnd) {
05109     PRInt32 nextRgIndex = mRowGroupIndex;
05110     do {
05111       nextRgIndex++;
05112       nsIFrame* frame = (nsTableRowGroupFrame*)mRowGroups.ElementAt(nextRgIndex); if (!frame) ABORT0();
05113       rg = mTableFrame.GetRowGroupFrame(frame);
05114       if (rg) {
05115         cellMap = mTableCellMap->GetMapFor(*rg); if (!cellMap) ABORT0();
05116         rgRowIndex = 0;
05117         nextRow = rg->GetFirstRow();
05118       }
05119     }
05120     while (rg && !nextRow);
05121     if(!rg) return;
05122   }
05123   else {
05124     // get the row within the same row group
05125     nextRow = mRow;
05126     for (PRInt32 i = 0; i < aRefInfo.rowSpan; i++) {
05127       nextRow = nextRow->GetNextRow(); if (!nextRow) ABORT0();
05128     }
05129   }
05130 
05131   CellData* cellData = cellMap->GetDataAt(*mTableCellMap, rgRowIndex, aColIndex, PR_TRUE);
05132   if (!cellData) { // add a dead cell data
05133     NS_ASSERTION(rgRowIndex < cellMap->GetRowCount(), "program error");
05134     nsRect damageArea;
05135     cellData = cellMap->AppendCell(*mTableCellMap, nsnull, rgRowIndex, PR_FALSE, damageArea); if (!cellData) ABORT0();
05136   }
05137   if (cellData->IsColSpan()) {
05138     aColIndex -= cellData->GetColSpanOffset();
05139     cellData = cellMap->GetDataAt(*mTableCellMap, rgRowIndex, aColIndex, PR_FALSE);
05140   }
05141   SetInfo(nextRow, aColIndex, cellData, aAjaInfo, cellMap);
05142 }
05143 
05144 // Assign priorities to border styles. For example, styleToPriority(NS_STYLE_BORDER_STYLE_SOLID)
05145 // will return the priority of NS_STYLE_BORDER_STYLE_SOLID. 
05146 static PRUint8 styleToPriority[13] = { 0,  // NS_STYLE_BORDER_STYLE_NONE
05147                                        3,  // NS_STYLE_BORDER_STYLE_GROOVE
05148                                        6,  // NS_STYLE_BORDER_STYLE_RIDGE
05149                                        7,  // NS_STYLE_BORDER_STYLE_DOTTED
05150                                        8,  // NS_STYLE_BORDER_STYLE_DASHED
05151                                        10, // NS_STYLE_BORDER_STYLE_SOLID
05152                                        11, // NS_STYLE_BORDER_STYLE_DOUBLE
05153                                        2,  // NS_STYLE_BORDER_STYLE_INSET
05154                                        5,  // NS_STYLE_BORDER_STYLE_OUTSET
05155                                        12, // NS_STYLE_BORDER_STYLE_HIDDEN
05156                                        1,  // NS_STYLE_BORDER_STYLE_BG_INSET
05157                                        4,  // NS_STYLE_BORDER_STYLE_BG_OUTSET
05158                                        9 };// NS_STYLE_BORDER_STYLE_BG_SOLID
05159 // priority rules follow CSS 2.1 spec
05160 // 'hidden', 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove',
05161 // and the lowest: 'inset'. none is even weaker
05162 #define CELL_CORNER PR_TRUE
05163 
05173 static void 
05174 GetColorAndStyle(const nsIFrame*  aFrame,
05175                  PRUint8          aSide,
05176                  PRUint8&         aStyle,
05177                  nscolor&         aColor,
05178                  PRBool           aTableIsLTR,
05179                  PRBool           aIgnoreTableEdge)
05180 {
05181   NS_PRECONDITION(aFrame, "null frame");
05182   // initialize out arg
05183   aColor = 0;
05184   const nsStyleBorder* styleData = aFrame->GetStyleBorder();
05185   if(!aTableIsLTR) { // revert the directions
05186     if (NS_SIDE_RIGHT == aSide) {
05187       aSide = NS_SIDE_LEFT;
05188     }
05189     else if (NS_SIDE_LEFT == aSide) {
05190       aSide = NS_SIDE_RIGHT;
05191     }
05192   }
05193   aStyle = styleData->GetBorderStyle(aSide);
05194 
05195   // if the rules marker is set, set the style either to none or remove the mask
05196   if (NS_STYLE_BORDER_STYLE_RULES_MARKER & aStyle) {
05197     if (aIgnoreTableEdge) {
05198       aStyle = NS_STYLE_BORDER_STYLE_NONE;
05199       return;
05200     }
05201     else {
05202       aStyle &= ~NS_STYLE_BORDER_STYLE_RULES_MARKER;
05203     }
05204   }
05205 
05206   if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
05207       (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
05208     return;
05209   }
05210   PRBool transparent, foreground;
05211   styleData->GetBorderColor(aSide, aColor, transparent, foreground);
05212   if (transparent) { 
05213     aColor = 0;
05214   }
05215   else if (foreground) {
05216     aColor = aFrame->GetStyleColor()->mColor;
05217   }
05218 }
05219 
05229 static void
05230 GetPaintStyleInfo(const nsIFrame*  aFrame,
05231                   PRUint8          aSide,
05232                   PRUint8&         aStyle,
05233                   nscolor&         aColor,
05234                   PRBool           aTableIsLTR,
05235                   PRBool           aIgnoreTableEdge)
05236 {
05237   GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR, aIgnoreTableEdge);
05238   if ((NS_STYLE_BORDER_STYLE_INSET    == aStyle) || 
05239       (NS_STYLE_BORDER_STYLE_BG_INSET == aStyle)) {
05240     aStyle = NS_STYLE_BORDER_STYLE_RIDGE;
05241   }
05242   else if ((NS_STYLE_BORDER_STYLE_OUTSET    == aStyle) || 
05243            (NS_STYLE_BORDER_STYLE_BG_OUTSET == aStyle)) {
05244     aStyle = NS_STYLE_BORDER_STYLE_GROOVE;
05245   }
05246 }
05247 
05260 static void
05261 GetColorAndStyle(const nsIFrame*  aFrame,
05262                  PRUint8          aSide,
05263                  PRUint8&         aStyle,
05264                  nscolor&         aColor,
05265                  PRBool           aTableIsLTR,
05266                  PRBool           aIgnoreTableEdge,
05267                  nscoord&         aWidth,
05268                  float            aTwipsToPixels)
05269 {
05270   GetColorAndStyle(aFrame, aSide, aStyle, aColor, aTableIsLTR, aIgnoreTableEdge);
05271   if ((NS_STYLE_BORDER_STYLE_NONE == aStyle) ||
05272       (NS_STYLE_BORDER_STYLE_HIDDEN == aStyle)) {
05273     aWidth = 0;
05274     return;
05275   }
05276   const nsStyleBorder* styleData = aFrame->GetStyleBorder();
05277   nscoord width;
05278   if(!aTableIsLTR) { // revert the directions
05279     if (NS_SIDE_RIGHT == aSide) {
05280       aSide = NS_SIDE_LEFT;
05281     }
05282     else if (NS_SIDE_LEFT == aSide) {
05283       aSide = NS_SIDE_RIGHT;
05284     }
05285   }
05286   styleData->CalcBorderFor(aFrame, aSide, width);
05287   aWidth = NSToCoordRound(aTwipsToPixels * (float)width);
05288 }
05289  
05290  
05291 /* BCCellBorder represents a border segment which can be either a horizontal
05292  * or a vertical segment. For each segment we need to know the color, width,
05293  * style, who owns it and how long it is in cellmap coordinates.
05294  * Ownership of these segments is  important to calculate which corners should
05295  * be bevelled. This structure has dual use, its used first to compute the
05296  * dominant border for horizontal and vertical segments and to store the
05297  * preliminary computed border results in the BCCellBorders structure.
05298  * This temporary storage is not symmetric with respect to horizontal and
05299  * vertical border segments, its always column oriented. For each column in
05300  * the cellmap there is a temporary stored vertical and horizontal segment.
05301  * XXX_Bernd this asymmetry is the root of those rowspan bc border errors
05302  */
05303 struct BCCellBorder
05304 {
05305   BCCellBorder() { Reset(0, 1); }
05306   void Reset(PRUint32 aRowIndex, PRUint32 aRowSpan);
05307   nscolor       color;    // border segment color
05308   nscoord       width;    // border segment width in pixel coordinates !!
05309   PRUint8       style;    // border segment style, possible values are defined
05310                           // in nsStyleConsts.h as NS_STYLE_BORDER_STYLE_*
05311   BCBorderOwner owner;    // border segment owner, possible values are defined
05312                           // in celldata.h. In the cellmap for each border
05313                           // segment we store the owner and later when
05314                           // painting we know the owner and can retrieve the
05315                           // style info from the corresponding frame
05316   PRInt32       rowIndex; // rowIndex of temporary stored horizontal border segments
05317   PRInt32       rowSpan;  // row span of temporary stored horizontal border segments
05318 };
05319 
05320 void
05321 BCCellBorder::Reset(PRUint32 aRowIndex,
05322                     PRUint32 aRowSpan)
05323 {
05324   style = NS_STYLE_BORDER_STYLE_NONE;
05325   color = 0;
05326   width = 0;
05327   owner = eTableOwner;
05328   rowIndex = aRowIndex;
05329   rowSpan  = aRowSpan;
05330 }
05331 
05332 // Compare two border segments, this comparison depends whether the two
05333 // segments meet at a corner and whether the second segment is horizontal.
05334 // The return value is whichever of aBorder1 or aBorder2 dominates.
05335 static const BCCellBorder&
05336 CompareBorders(PRBool              aIsCorner, // Pass PR_TRUE for corner calculations
05337                const BCCellBorder& aBorder1,
05338                const BCCellBorder& aBorder2,
05339                PRBool              aSecondIsHorizontal,
05340                PRBool*             aFirstDominates = nsnull)
05341 {
05342   PRBool firstDominates = PR_TRUE;
05343   
05344   if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder1.style) {
05345     firstDominates = (aIsCorner) ? PR_FALSE : PR_TRUE;
05346   }
05347   else if (NS_STYLE_BORDER_STYLE_HIDDEN == aBorder2.style) {
05348     firstDominates = (aIsCorner) ? PR_TRUE : PR_FALSE;
05349   }
05350   else if (aBorder1.width < aBorder2.width) {
05351     firstDominates = PR_FALSE;
05352   }
05353   else if (aBorder1.width == aBorder2.width) {
05354     if (styleToPriority[aBorder1.style] < styleToPriority[aBorder2.style]) {
05355       firstDominates = PR_FALSE;
05356     }
05357     else if (styleToPriority[aBorder1.style] == styleToPriority[aBorder2.style]) {
05358       if (aBorder1.owner == aBorder2.owner) {
05359         firstDominates = !aSecondIsHorizontal;
05360       }
05361       else if (aBorder1.owner < aBorder2.owner) {
05362         firstDominates = PR_FALSE;
05363       }
05364     }
05365   }
05366 
05367   if (aFirstDominates)
05368     *aFirstDominates = firstDominates;
05369 
05370   if (firstDominates)
05371     return aBorder1;
05372   return aBorder2;
05373 }
05374 
05398 static BCCellBorder
05399 CompareBorders(const nsIFrame*  aTableFrame,
05400                const nsIFrame*  aColGroupFrame,
05401                const nsIFrame*  aColFrame,
05402                const nsIFrame*  aRowGroupFrame,
05403                const nsIFrame*  aRowFrame,
05404                const nsIFrame*  aCellFrame,
05405                PRBool           aTableIsLTR,
05406                PRBool           aIgnoreTableEdge,
05407                PRUint8          aSide,
05408                PRBool           aAja,
05409                float            aTwipsToPixels)
05410 {
05411   BCCellBorder border, tempBorder;
05412   PRBool horizontal = (NS_SIDE_TOP == aSide) || (NS_SIDE_BOTTOM == aSide);
05413 
05414   // start with the table as dominant if present
05415   if (aTableFrame) {
05416     GetColorAndStyle(aTableFrame, aSide, border.style, border.color, aTableIsLTR, aIgnoreTableEdge, border.width, aTwipsToPixels);
05417     border.owner = eTableOwner;
05418     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
05419       return border;
05420     }
05421   }
05422   // see if the colgroup is dominant
05423   if (aColGroupFrame) {
05424     GetColorAndStyle(aColGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width, aTwipsToPixels);
05425     tempBorder.owner = (aAja && !horizontal) ? eAjaColGroupOwner : eColGroupOwner;
05426     // pass here and below PR_FALSE for aSecondIsHorizontal as it is only used for corner calculations.
05427     border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
05428     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
05429       return border;
05430     }
05431   }
05432   // see if the col is dominant
05433   if (aColFrame) {
05434     GetColorAndStyle(aColFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width, aTwipsToPixels);
05435     tempBorder.owner = (aAja && !horizontal) ? eAjaColOwner : eColOwner;
05436     border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
05437     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
05438       return border;
05439     }
05440   }
05441   // see if the rowgroup is dominant
05442   if (aRowGroupFrame) {
05443     GetColorAndStyle(aRowGroupFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width, aTwipsToPixels);
05444     tempBorder.owner = (aAja && horizontal) ? eAjaRowGroupOwner : eRowGroupOwner;
05445     border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
05446     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
05447       return border;
05448     }
05449   }
05450   // see if the row is dominant
05451   if (aRowFrame) {
05452     GetColorAndStyle(aRowFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width, aTwipsToPixels);
05453     tempBorder.owner = (aAja && horizontal) ? eAjaRowOwner : eRowOwner;
05454     border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
05455     if (NS_STYLE_BORDER_STYLE_HIDDEN == border.style) {
05456       return border;
05457     }
05458   }
05459   // see if the cell is dominant
05460   if (aCellFrame) {
05461     GetColorAndStyle(aCellFrame, aSide, tempBorder.style, tempBorder.color, aTableIsLTR, aIgnoreTableEdge, tempBorder.width, aTwipsToPixels);
05462     tempBorder.owner = (aAja) ? eAjaCellOwner : eCellOwner;
05463     border = CompareBorders(!CELL_CORNER, border, tempBorder, PR_FALSE);
05464   }
05465   return border;
05466 }
05467 
05468 static PRBool 
05469 Perpendicular(PRUint8 aSide1, 
05470               PRUint8 aSide2)
05471 {
05472   switch (aSide1) {
05473   case NS_SIDE_TOP:
05474     return (NS_SIDE_BOTTOM != aSide2);
05475   case NS_SIDE_RIGHT:
05476     return (NS_SIDE_LEFT != aSide2);
05477   case NS_SIDE_BOTTOM:
05478     return (NS_SIDE_TOP != aSide2);
05479   default: // NS_SIDE_LEFT
05480     return (NS_SIDE_RIGHT != aSide2);
05481   }
05482 }
05483 
05484 // XXX allocate this as number-of-cols+1 instead of number-of-cols+1 * number-of-rows+1
05485 struct BCCornerInfo 
05486 {
05487   BCCornerInfo() { ownerColor = 0; ownerWidth = subWidth = ownerSide = ownerElem = subSide = 
05488                    subElem = hasDashDot = numSegs = bevel = 0;
05489                    ownerStyle = 0xFF; subStyle = NS_STYLE_BORDER_STYLE_SOLID;  }
05490   void Set(PRUint8       aSide,
05491            BCCellBorder  border);
05492 
05493   void Update(PRUint8       aSide,
05494               BCCellBorder  border);
05495 
05496   nscolor   ownerColor;     // color of borderOwner
05497   PRUint16  ownerWidth;     // pixel width of borderOwner 
05498   PRUint16  subWidth;       // pixel width of the largest border intersecting the border perpendicular 
05499                             // to ownerSide
05500   PRUint32  ownerSide:2;    // side (e.g NS_SIDE_TOP, NS_SIDE_RIGHT, etc) of the border owning 
05501                             // the corner relative to the corner
05502   PRUint32  ownerElem:3;    // elem type (e.g. eTable, eGroup, etc) owning the corner
05503   PRUint32  ownerStyle:8;   // border style of ownerElem
05504   PRUint32  subSide:2;      // side of border with subWidth relative to the corner
05505   PRUint32  subElem:3;      // elem type (e.g. eTable, eGroup, etc) of sub owner
05506   PRUint32  subStyle:8;     // border style of subElem
05507   PRUint32  hasDashDot:1;   // does a dashed, dotted segment enter the corner, they cannot be beveled
05508   PRUint32  numSegs:3;      // number of segments entering corner
05509   PRUint32  bevel:1;        // is the corner beveled (uses the above two fields together with subWidth)
05510   PRUint32  unused:1;
05511 };
05512 
05513 void 
05514 BCCornerInfo::Set(PRUint8       aSide,
05515                   BCCellBorder  aBorder)
05516 {
05517   ownerElem  = aBorder.owner;
05518   ownerStyle = aBorder.style;
05519   ownerWidth = aBorder.width;
05520   ownerColor = aBorder.color;
05521   ownerSide  = aSide;
05522   hasDashDot = 0;
05523   numSegs    = 0;
05524   if (aBorder.width > 0) {
05525     numSegs++;
05526     hasDashDot = (NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
05527                  (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style);
05528   }
05529   bevel      = 0;
05530   subWidth   = 0;
05531   // the following will get set later
05532   subSide    = ((aSide == NS_SIDE_LEFT) || (aSide == NS_SIDE_RIGHT)) ? NS_SIDE_TOP : NS_SIDE_LEFT; 
05533   subElem    = eTableOwner;
05534   subStyle   = NS_STYLE_BORDER_STYLE_SOLID; 
05535 }
05536 
05537 void 
05538 BCCornerInfo::Update(PRUint8       aSide,
05539                      BCCellBorder  aBorder)
05540 {
05541   PRBool existingWins = PR_FALSE;
05542   if (0xFF == ownerStyle) { // initial value indiating that it hasn't been set yet
05543     Set(aSide, aBorder);
05544   }
05545   else {
05546     PRBool horizontal = (NS_SIDE_LEFT == aSide) || (NS_SIDE_RIGHT == aSide); // relative to the corner
05547     BCCellBorder oldBorder, tempBorder;
05548     oldBorder.owner  = (BCBorderOwner) ownerElem;
05549     oldBorder.style =  ownerStyle;
05550     oldBorder.width =  ownerWidth;
05551     oldBorder.color =  ownerColor;
05552 
05553     PRUint8 oldSide  = ownerSide;
05554     
05555     tempBorder = CompareBorders(CELL_CORNER, oldBorder, aBorder, horizontal, &existingWins); 
05556                          
05557     ownerElem  = tempBorder.owner;
05558     ownerStyle = tempBorder.style;
05559     ownerWidth = tempBorder.width;
05560     ownerColor = tempBorder.color;
05561     if (existingWins) { // existing corner is dominant
05562       if (::Perpendicular(ownerSide, aSide)) {
05563         // see if the new sub info replaces the old
05564         BCCellBorder subBorder;
05565         subBorder.owner = (BCBorderOwner) subElem;
05566         subBorder.style =  subStyle;
05567         subBorder.width =  subWidth;
05568         subBorder.color = 0; // we are not interested in subBorder color
05569         PRBool firstWins;
05570 
05571         tempBorder = CompareBorders(CELL_CORNER, subBorder, aBorder, horizontal, &firstWins);
05572         
05573         subElem  = tempBorder.owner;
05574         subStyle = tempBorder.style;
05575         subWidth = tempBorder.width;
05576         if (!firstWins) {
05577           subSide = aSide; 
05578         }
05579       }
05580     }
05581     else { // input args are dominant
05582       ownerSide = aSide;
05583       if (::Perpendicular(oldSide, ownerSide)) {
05584         subElem  = oldBorder.owner;
05585         subStyle = oldBorder.style;
05586         subWidth = oldBorder.width;
05587         subSide  = oldSide;
05588       }
05589     }
05590     if (aBorder.width > 0) {
05591       numSegs++;
05592       if (!hasDashDot && ((NS_STYLE_BORDER_STYLE_DASHED == aBorder.style) ||
05593                           (NS_STYLE_BORDER_STYLE_DOTTED == aBorder.style))) {
05594         hasDashDot = 1;
05595       }
05596     }
05597   
05598     // bevel the corner if only two perpendicular non dashed/dotted segments enter the corner
05599     bevel = (2 == numSegs) && (subWidth > 1) && (0 == hasDashDot);
05600   }
05601 }
05602 
05603 struct BCCorners
05604 {
05605   BCCorners(PRInt32 aNumCorners,
05606             PRInt32 aStartIndex);
05607 
05608   ~BCCorners() { delete [] corners; }
05609   
05610   BCCornerInfo& operator [](PRInt32 i) const
05611   { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
05612     return corners[PR_MAX(PR_MIN(i, endIndex), startIndex) - startIndex]; }
05613 
05614   PRInt32       startIndex;
05615   PRInt32       endIndex;
05616   BCCornerInfo* corners;
05617 };
05618   
05619 BCCorners::BCCorners(PRInt32 aNumCorners,
05620                      PRInt32 aStartIndex)
05621 {
05622   NS_ASSERTION((aNumCorners > 0) && (aStartIndex >= 0), "program error");
05623   startIndex = aStartIndex;
05624   endIndex   = aStartIndex + aNumCorners - 1;
05625   corners    = new BCCornerInfo[aNumCorners]; 
05626 }
05627 
05628 
05629 struct BCCellBorders
05630 {
05631   BCCellBorders(PRInt32 aNumBorders,
05632                 PRInt32 aStartIndex);
05633 
05634   ~BCCellBorders() { delete [] borders; }
05635   
05636   BCCellBorder& operator [](PRInt32 i) const
05637   { NS_ASSERTION((i >= startIndex) && (i <= endIndex), "program error");
05638     return borders[PR_MAX(PR_MIN(i, endIndex), startIndex) - startIndex]; }
05639 
05640   PRInt32       startIndex;
05641   PRInt32       endIndex;
05642   BCCellBorder* borders;
05643 };
05644   
05645 BCCellBorders::BCCellBorders(PRInt32 aNumBorders,
05646                              PRInt32 aStartIndex)
05647 {
05648   NS_ASSERTION((aNumBorders > 0) && (aStartIndex >= 0), "program error");
05649   startIndex = aStartIndex;
05650   endIndex   = aStartIndex + aNumBorders - 1;
05651   borders    = new BCCellBorder[aNumBorders]; 
05652 }
05653 
05654 // this function sets the new border properties and returns true if the border
05655 // segment will start a new segment and not prolong the existing segment.
05656 static PRBool
05657 SetBorder(const BCCellBorder&   aNewBorder,
05658           BCCellBorder&         aBorder)
05659 {
05660   PRBool changed = (aNewBorder.style != aBorder.style) ||
05661                    (aNewBorder.width != aBorder.width) ||
05662                    (aNewBorder.color != aBorder.color);
05663   aBorder.color        = aNewBorder.color;
05664   aBorder.width        = aNewBorder.width;
05665   aBorder.style        = aNewBorder.style;
05666   aBorder.owner        = aNewBorder.owner;
05667 
05668   return changed;
05669 }
05670 
05671 // this function will set the horizontal border. It will return true if the 
05672 // existing segment will not be continued. Having a vertical owner of a corner
05673 // should also start a new segment.
05674 static PRBool
05675 SetHorBorder(const BCCellBorder& aNewBorder,
05676              const BCCornerInfo& aCorner,
05677              BCCellBorder&       aBorder)
05678 {
05679   PRBool startSeg = ::SetBorder(aNewBorder, aBorder);
05680   if (!startSeg) {
05681     startSeg = ((NS_SIDE_LEFT != aCorner.ownerSide) && (NS_SIDE_RIGHT != aCorner.ownerSide));
05682   }
05683   return startSeg;
05684 }
05685 
05686 // Make the damage area larger on the top and bottom by at least one row and on the left and right 
05687 // at least one column. This is done so that adjacent elements are part of the border calculations. 
05688 // The extra segments and borders outside the actual damage area will not be updated in the cell map, 
05689 // because they in turn would need info from adjacent segments outside the damage area to be accurate.
05690 void
05691 nsTableFrame::ExpandBCDamageArea(nsRect& aRect) const
05692 {
05693   PRInt32 numRows = GetRowCount();
05694   PRInt32 numCols = GetColCount();
05695 
05696   PRInt32 dStartX = aRect.x;
05697   PRInt32 dEndX   = aRect.XMost() - 1;
05698   PRInt32 dStartY = aRect.y;
05699   PRInt32 dEndY   = aRect.YMost() - 1;
05700 
05701   // expand the damage area in each direction
05702   if (dStartX > 0) {
05703     dStartX--;
05704   }
05705   if (dEndX < (numCols - 1)) {
05706     dEndX++;
05707   }
05708   if (dStartY > 0) {
05709     dStartY--;
05710   }
05711   if (dEndY < (numRows - 1)) {
05712     dEndY++;
05713   }
05714   // Check the damage area so that there are no cells spanning in or out. If there are any then
05715   // make the damage area as big as the table, similarly to the way the cell map decides whether
05716   // to rebuild versus expand. This could be optimized to expand to the smallest area that contains
05717   // no spanners, but it may not be worth the effort in general, and it would need to be done in the
05718   // cell map as well.
05719   PRBool haveSpanner = PR_FALSE;
05720   if ((dStartX > 0) || (dEndX < (numCols - 1)) || (dStartY > 0) || (dEndY < (numRows - 1))) {
05721     nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
05722     // Get the ordered row groups 
05723     PRUint32 numRowGroups;
05724     nsVoidArray rowGroups;
05725     OrderRowGroups(rowGroups, numRowGroups, nsnull);
05726     for (PRUint32 rgX = 0; rgX < numRowGroups; rgX++) {
05727       nsIFrame* kidFrame = (nsIFrame*)rowGroups.ElementAt(rgX);
05728       nsTableRowGroupFrame* rgFrame = GetRowGroupFrame(kidFrame); if (!rgFrame) ABORT0();
05729       PRInt32 rgStartY = rgFrame->GetStartRowIndex();
05730       PRInt32 rgEndY   = rgStartY + rgFrame->GetRowCount() - 1;
05731       if (dEndY < rgStartY) 
05732         break;
05733       nsCellMap* cellMap = tableCellMap->GetMapFor(*rgFrame); if (!cellMap) ABORT0();
05734       // check for spanners from above and below
05735       if ((dStartY > 0) && (dStartY >= rgStartY) && (dStartY <= rgEndY)) {
05736         nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(dStartY - rgStartY); if (!row) ABORT0();
05737         for (PRInt32 x = dStartX; x <= dEndX; x++) {
05738           CellData* cellData = (row->Count() > x) ? (CellData*)row->ElementAt(x) : nsnull;
05739           if (cellData && (cellData->IsRowSpan())) {
05740              haveSpanner = PR_TRUE;
05741              break;
05742           }
05743         }
05744         if (dEndY < rgEndY) {
05745           row = (nsVoidArray*)cellMap->mRows.ElementAt(dEndY + 1 - rgStartY); if (!row) ABORT0();
05746           for (PRInt32 x = dStartX; x <= dEndX; x++) {
05747             CellData* cellData = (CellData*)row->SafeElementAt(x);
05748             if (cellData && (cellData->IsRowSpan())) {
05749               haveSpanner = PR_TRUE;
05750               break;
05751             }
05752           }
05753         }
05754       }
05755       // check for spanners on the left and right
05756       PRInt32 iterStartY = -1;
05757       PRInt32 iterEndY   = -1;
05758       if ((dStartY >= rgStartY) && (dStartY <= rgEndY)) {
05759         // the damage area starts in the row group
05760         iterStartY = dStartY;
05761         iterEndY   = PR_MIN(dEndY, rgEndY);
05762       }
05763       else if ((dEndY >= rgStartY) && (dEndY <= rgEndY)) {
05764         // the damage area ends in the row group
05765         iterStartY = rgStartY;
05766         iterEndY   = PR_MIN(dEndY, rgStartY);
05767       }
05768       else if ((rgStartY >= dStartY) && (rgEndY <= dEndY)) {
05769         // the damage area contains the row group
05770         iterStartY = rgStartY;
05771         iterEndY   = rgEndY;
05772       }
05773       if ((iterStartY >= 0) && (iterEndY >= 0)) {
05774         for (PRInt32 y = iterStartY; y <= iterEndY; y++) {
05775           nsVoidArray* row = (nsVoidArray*)cellMap->mRows.ElementAt(y - rgStartY); if (!row) ABORT0();
05776           CellData* cellData = (CellData*)row->SafeElementAt(dStartX);
05777           if (cellData && (cellData->IsColSpan())) {
05778             haveSpanner = PR_TRUE;
05779             break;
05780           }
05781           if (dEndX < (numCols - 1)) {
05782             cellData = (CellData*)row->SafeElementAt(dEndX + 1);
05783             if (cellData && (cellData->IsColSpan())) {
05784               haveSpanner = PR_TRUE;
05785               break;
05786             }
05787           }
05788         }
05789       }
05790     }
05791   }
05792   if (haveSpanner) {
05793     // make the damage area the whole table
05794     aRect.x      = 0;
05795     aRect.y      = 0;
05796     aRect.width  = numCols;
05797     aRect.height = numRows;
05798   }
05799   else {
05800     aRect.x      = dStartX;
05801     aRect.y      = dStartY;
05802     aRect.width  = 1 + dEndX - dStartX;
05803     aRect.height = 1 + dEndY - dStartY;
05804   }
05805 }
05806 
05807 #define MAX_TABLE_BORDER_WIDTH 255
05808 static PRUint8
05809 LimitBorderWidth(PRUint16 aWidth)
05810 {
05811   return PR_MIN(MAX_TABLE_BORDER_WIDTH, aWidth);
05812 }
05813 
05814 /* Here is the order for storing border edges in the cell map as a cell is processed. There are 
05815    n=colspan top and bottom border edges per cell and n=rowspan left and right border edges per cell.
05816 
05817    1) On the top edge of the table, store the top edge. Never store the top edge otherwise, since
05818       a bottom edge from a cell above will take care of it.
05819    2) On the left edge of the table, store the left edge. Never store the left edge othewise, since
05820       a right edge from a cell to the left will take care of it.
05821    3) Store the right edge (or edges if a row span) 
05822    4) Store the bottom edge (or edges if a col span)
05823     
05824    Since corners are computed with only an array of BCCornerInfo indexed by the number-of-cols, corner
05825    calculations are somewhat complicated. Using an array with number-of-rows * number-of-col entries
05826    would simplify this, but at an extra in memory cost of nearly 12 bytes per cell map entry. Collapsing 
05827    borders already have about an extra 8 byte per cell map entry overhead (this could be
05828    reduced to 4 bytes if we are willing to not store border widths in nsTableCellFrame), Here are the 
05829    rules in priority order for storing cornes in the cell map as a cell is processed. top-left means the
05830    left endpoint of the border edge on the top of the cell. There are n=colspan top and bottom border 
05831    edges per cell and n=rowspan left and right border edges per cell.
05832 
05833    1) On the top edge of the table, store the top-left corner, unless on the left edge of the table.
05834       Never store the top-right corner, since it will get stored as a right-top corner.
05835    2) On the left edge of the table, store the left-top corner. Never store the left-bottom corner,
05836       since it will get stored as a bottom-left corner.
05837    3) Store the right-top corner if (a) it is the top right corner of the table or (b) it is not on
05838       the top edge of the table. Never store the right-bottom corner since it will get stored as a 
05839       bottom-right corner.
05840    4) Store the bottom-right corner, if it is the bottom right corner of the table. Never store it 
05841       otherwise, since it will get stored as either a right-top corner by a cell below or
05842       a bottom-left corner from a cell to the right.
05843    5) Store the bottom-left corner, if (a) on the bottom edge of the table or (b) if the left edge hits 
05844       the top side of a colspan in its interior. Never store the corner otherwise, since it will 
05845       get stored as a right-top corner by a cell from below.
05846 
05847    XXX the BC-RTL hack - The correct fix would be a rewrite as described in bug 203686.
05848    In order to draw borders in rtl conditions somehow correct, the existing structure which relies
05849    heavily on the assumption that the next cell sibling will be on the right side, has been modified.
05850    We flip the border during painting and during style lookup. Look for tableIsLTR for places where
05851    the flipping is done.
05852  */
05853 
05854 #define TOP_DAMAGED(aRowIndex)    ((aRowIndex) >= propData->mDamageArea.y) 
05855 #define RIGHT_DAMAGED(aColIndex)  ((aColIndex) <  propData->mDamageArea.XMost()) 
05856 #define BOTTOM_DAMAGED(aRowIndex) ((aRowIndex) <  propData->mDamageArea.YMost()) 
05857 #define LEFT_DAMAGED(aColIndex)   ((aColIndex) >= propData->mDamageArea.x) 
05858 
05859 #define TABLE_EDGE  PR_TRUE
05860 #define ADJACENT    PR_TRUE
05861 #define HORIZONTAL  PR_TRUE
05862 
05863 // Calc the dominant border at every cell edge and corner within the current damage area
05864 void 
05865 nsTableFrame::CalcBCBorders()
05866 {
05867   nsTableCellMap* tableCellMap = GetCellMap(); if (!tableCellMap) ABORT0();
05868   PRInt32 numRows = GetRowCount();
05869   PRInt32 numCols = GetColCount();
05870   
05871   // Get the property holding the table damage area and border widths
05872   BCPropertyData* propData = 
05873     (BCPropertyData*)nsTableFrame::GetProperty(this, nsLayoutAtoms::tableBCProperty, PR_FALSE);
05874   if (!propData) ABORT0();
05875 
05876   PRBool tableIsLTR = GetStyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
05877   PRUint8 firstSide, secondSide;
05878   if (tableIsLTR) {
05879     firstSide  = NS_SIDE_LEFT;
05880     secondSide = NS_SIDE_RIGHT;
05881   }
05882   else {
05883     firstSide  = NS_SIDE_RIGHT;
05884     secondSide = NS_SIDE_LEFT;
05885   }
05886   CheckFixDamageArea(numRows, numCols, propData->mDamageArea);
05887   // calculate an expanded damage area 
05888   nsRect damageArea(propData->mDamageArea);
05889   ExpandBCDamageArea(damageArea);
05890 
05891   // segments that are on the table border edges need to be initialized only once
05892   PRBool tableBorderReset[4];
05893   for (PRUint32 sideX = NS_SIDE_TOP; sideX <= NS_SIDE_LEFT; sideX++) {
05894     tableBorderReset[sideX] = PR_FALSE;
05895   }
05896   GET_TWIPS_TO_PIXELS(GetPresContext(), t2p);
05897 
05898   // vertical borders indexed in x-direction (cols)
05899   BCCellBorders lastVerBorders(damageArea.width + 1, damageArea.x); if (!lastVerBorders.borders) ABORT0();
05900   BCCellBorder  lastTopBorder, lastBottomBorder;
05901   // horizontal borders indexed in x-direction (cols)
05902   BCCellBorders lastBottomBorders(damageArea.width + 1, damageArea.x); if (!lastBottomBorders.borders) ABORT0();
05903   PRBool startSeg;
05904   PRBool gotRowBorder = PR_FALSE;
05905 
05906   BCMapCellInfo  info, ajaInfo;
05907   BCCellBorder currentBorder, adjacentBorder;
05908   PRInt32   cellEndRowIndex = -1;
05909   PRInt32   cellEndColIndex = -1;
05910   BCCorners topCorners(damageArea.width + 1, damageArea.x); if (!topCorners.corners) ABORT0();
05911   BCCorners bottomCorners(damageArea.width + 1, damageArea.x); if (!bottomCorners.corners) ABORT0();
05912 
05913   BCMapCellIterator iter(*this, damageArea);
05914   for (iter.First(info); !iter.mAtEnd; iter.Next(info)) {
05915 
05916     cellEndRowIndex = info.rowIndex + info.rowSpan - 1;
05917     cellEndColIndex = info.colIndex + info.colSpan - 1;
05918     
05919     PRBool bottomRowSpan = PR_FALSE;
05920     // see if lastTopBorder, lastBottomBorder need to be reset
05921     if (iter.IsNewRow()) { 
05922       gotRowBorder = PR_FALSE;
05923       lastTopBorder.Reset(info.rowIndex, info.rowSpan);
05924       lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan);
05925     }
05926     else if (info.colIndex > damageArea.x) {
05927       lastBottomBorder = lastBottomBorders[info.colIndex - 1];
05928       if (info.rowIndex > lastBottomBorder.rowIndex - lastBottomBorder.rowSpan) { 
05929         // the top border's left edge butts against the middle of a rowspan
05930         lastTopBorder.Reset(info.rowIndex, info.rowSpan);
05931       }
05932       if (lastBottomBorder.rowIndex > (cellEndRowIndex + 1)) {
05933         // the bottom border's left edge butts against the middle of a rowspan
05934         lastBottomBorder.Reset(cellEndRowIndex + 1, info.rowSpan);
05935         bottomRowSpan = PR_TRUE;
05936       }
05937     }
05938 
05939     // find the dominant border considering the cell's top border and the table, row group, row
05940     // if the border is at the top of the table, otherwise it was processed in a previous row
05941     if (0 == info.rowIndex) {
05942       if (!tableBorderReset[NS_SIDE_TOP]) {
05943         propData->mTopBorderWidth = 0;
05944         tableBorderReset[NS_SIDE_TOP] = PR_TRUE;
05945       }
05946       for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) {
05947         nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0();
05948         nsIFrame* cgFrame = colFrame->GetParent(); if (!cgFrame) ABORT0();
05949         currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg, info.topRow,
05950                                        info.cell, tableIsLTR, TABLE_EDGE, NS_SIDE_TOP,
05951                                        !ADJACENT, t2p);
05952         // update/store the top left & top right corners of the seg 
05953         BCCornerInfo& tlCorner = topCorners[colX]; // top left
05954         if (0 == colX) {
05955           tlCorner.Set(NS_SIDE_RIGHT, currentBorder); // we are on right hand side of the corner
05956         }
05957         else {
05958           tlCorner.Update(NS_SIDE_RIGHT, currentBorder);
05959           tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, 0, 0, colX,
05960                                           tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel);
05961         }
05962         topCorners[colX + 1].Set(NS_SIDE_LEFT, currentBorder); // top right
05963         // update lastTopBorder and see if a new segment starts
05964         startSeg = SetHorBorder(currentBorder, tlCorner, lastTopBorder);
05965         // store the border segment in the cell map
05966         tableCellMap->SetBCBorderEdge(NS_SIDE_TOP, *info.cellMap, 0, 0, colX,
05967                                       1, currentBorder.owner, currentBorder.width, startSeg);
05968         // update the affected borders of the cell, row, and table
05969         if (info.cell) {
05970           info.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(NS_SIDE_TOP)));
05971         }
05972         if (info.topRow) {
05973           BCPixelSize half = BC_BORDER_BOTTOM_HALF(currentBorder.width);
05974           info.topRow->SetTopBCBorderWidth(PR_MAX(half, info.topRow->GetTopBCBorderWidth()));
05975         }
05976         propData->mTopBorderWidth = LimitBorderWidth(PR_MAX(propData->mTopBorderWidth, (PRUint8)currentBorder.width));
05977         //calculate column continuous borders
05978         //we only need to do this once, so we'll do it only on the first row
05979         currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg,
05980                                        info.topRow, nsnull, tableIsLTR, 
05981                                        TABLE_EDGE, NS_SIDE_TOP, !ADJACENT, t2p);
05982         ((nsTableColFrame*)colFrame)->SetContinuousBCBorderWidth(NS_SIDE_TOP,
05983                                                                  currentBorder.width);
05984         if (numCols == cellEndColIndex + 1) {
05985           currentBorder = CompareBorders(this, cgFrame, colFrame, nsnull,
05986                                          nsnull, nsnull, tableIsLTR, TABLE_EDGE,
05987                                          NS_SIDE_RIGHT, !ADJACENT, t2p);
05988         }
05989         else {
05990           currentBorder = CompareBorders(nsnull, cgFrame, colFrame, nsnull,
05991                                          nsnull, nsnull, tableIsLTR, !TABLE_EDGE,
05992                                          NS_SIDE_RIGHT, !ADJACENT, t2p);
05993         }
05994         ((nsTableColFrame*)colFrame)->SetContinuousBCBorderWidth(NS_SIDE_RIGHT,
05995                                                                  currentBorder.width);
05996         
05997       }
05998       //calculate continuous top first row & rowgroup border: special case
05999       //because it must include the table in the collapse
06000       if (info.topRow) {
06001         currentBorder = CompareBorders(this, nsnull, nsnull, info.rg,
06002                                        info.topRow, nsnull, tableIsLTR,
06003                                        TABLE_EDGE, NS_SIDE_TOP, !ADJACENT, t2p);
06004         info.topRow->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
06005       }
06006       if (info.cgRight && info.cg) {
06007         //calculate continuous top colgroup border once per colgroup
06008         currentBorder = CompareBorders(this, info.cg, nsnull, info.rg,
06009                                        info.topRow, nsnull, tableIsLTR, 
06010                                        TABLE_EDGE, NS_SIDE_TOP, !ADJACENT, t2p);
06011         info.cg->SetContinuousBCBorderWidth(NS_SIDE_TOP, currentBorder.width);
06012       }
06013       if (0 == info.colIndex) {
06014         currentBorder = CompareBorders(this, info.cg, info.leftCol, nsnull,
06015                                        nsnull, nsnull, tableIsLTR, TABLE_EDGE,
06016                                        NS_SIDE_LEFT, !ADJACENT, t2p);
06017         mBits.mLeftContBCBorder = currentBorder.width;
06018       }
06019     }
06020     else {
06021       // see if the top border needs to be the start of a segment due to a vertical border owning the corner
06022       if (info.colIndex > 0) {
06023         BCData& data = ((BCCellData*)info.cellData)->mData;
06024         if (!data.IsTopStart()) {
06025           PRUint8 cornerSide;
06026           PRPackedBool bevel;
06027           data.GetCorner(cornerSide, bevel);
06028           if ((NS_SIDE_TOP == cornerSide) || (NS_SIDE_BOTTOM == cornerSide)) {
06029             data.SetTopStart(PR_TRUE);
06030           }
06031         }
06032       }  
06033     }
06034 
06035     // find the dominant border considering the cell's left border and the table, col group, col  
06036     // if the border is at the left of the table, otherwise it was processed in a previous col
06037     if (0 == info.colIndex) {
06038       if (!tableBorderReset[NS_SIDE_LEFT]) {
06039         propData->mLeftBorderWidth = 0;
06040         tableBorderReset[NS_SIDE_LEFT] = PR_TRUE;
06041       }
06042       nsTableRowFrame* rowFrame = nsnull;
06043       for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) {
06044         rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow();
06045         currentBorder = CompareBorders(this, info.cg, info.leftCol, info.rg, rowFrame, info.cell, 
06046                                        tableIsLTR, TABLE_EDGE, NS_SIDE_LEFT, !ADJACENT, t2p);
06047         BCCornerInfo& tlCorner = (0 == rowX) ? topCorners[0] : bottomCorners[0]; // top left
06048         tlCorner.Update(NS_SIDE_BOTTOM, currentBorder);
06049         tableCellMap->SetBCBorderCorner(eTopLeft, *info.cellMap, iter.mRowGroupStart, rowX, 
06050                                         0, tlCorner.ownerSide, tlCorner.subWidth, tlCorner.bevel);
06051         bottomCorners[0].Set(NS_SIDE_TOP, currentBorder); // bottom left             
06052         // update lastVerBordersBorder and see if a new segment starts
06053         startSeg = SetBorder(currentBorder, lastVerBorders[0]);
06054         // store the border segment in the cell map 
06055         tableCellMap->SetBCBorderEdge(NS_SIDE_LEFT, *info.cellMap, iter.mRowGroupStart, rowX, 
06056                                       info.colIndex, 1, currentBorder.owner, currentBorder.width, startSeg);
06057         // update the left border of the cell, col and table
06058         if (info.cell) {
06059           info.cell->SetBorderWidth(firstSide, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(firstSide)));
06060         }
06061         if (info.leftCol) {
06062           BCPixelSize half = BC_BORDER_RIGHT_HALF(currentBorder.width);
06063           info.leftCol->SetLeftBorderWidth(PR_MAX(half, info.leftCol->GetLeftBorderWidth()));
06064         }
06065         propData->mLeftBorderWidth = LimitBorderWidth(PR_MAX(propData->mLeftBorderWidth, currentBorder.width));
06066         //get row continuous borders
06067         if (rowFrame) {
06068           currentBorder = CompareBorders(this, info.cg, info.leftCol,
06069                                          info.rg, rowFrame, nsnull, tableIsLTR,
06070                                          TABLE_EDGE, NS_SIDE_LEFT, !ADJACENT, t2p);
06071           rowFrame->SetContinuousBCBorderWidth(firstSide, currentBorder.width);
06072         }
06073       }
06074       //get row group continuous borders
06075       if (info.rgBottom && info.rg) { //once per row group, so check for bottom
06076         currentBorder = CompareBorders(this, info.cg, info.leftCol, info.rg, nsnull,
06077                                        nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_LEFT,
06078                                        !ADJACENT, t2p);
06079         info.rg->SetContinuousBCBorderWidth(firstSide, currentBorder.width);
06080       }
06081     }
06082 
06083     // find the dominant border considering the cell's right border, adjacent cells and the table, row group, row
06084     if (numCols == cellEndColIndex + 1) { // touches right edge of table
06085       if (!tableBorderReset[NS_SIDE_RIGHT]) {
06086         propData->mRightBorderWidth = 0;
06087         tableBorderReset[NS_SIDE_RIGHT] = PR_TRUE;
06088       }
06089       nsTableRowFrame* rowFrame = nsnull;
06090       for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX++) {
06091         rowFrame = (rowX == info.rowIndex) ? info.topRow : rowFrame->GetNextRow();
06092         currentBorder = CompareBorders(this, info.cg, info.rightCol, info.rg, rowFrame, info.cell, 
06093                                        tableIsLTR, TABLE_EDGE, NS_SIDE_RIGHT, ADJACENT, t2p);
06094         // update/store the top right & bottom right corners 
06095         BCCornerInfo& trCorner = (0 == rowX) ? topCorners[cellEndColIndex + 1] : bottomCorners[cellEndColIndex + 1]; 
06096         trCorner.Update(NS_SIDE_BOTTOM, currentBorder);   // top right
06097         tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX, 
06098                                         cellEndColIndex, trCorner.ownerSide, trCorner.subWidth, trCorner.bevel);
06099         BCCornerInfo& brCorner = bottomCorners[cellEndColIndex + 1];
06100         brCorner.Set(NS_SIDE_TOP, currentBorder); // bottom right
06101         tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rowX,
06102                                         cellEndColIndex, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel);
06103         // update lastVerBorders and see if a new segment starts
06104         startSeg = SetBorder(currentBorder, lastVerBorders[cellEndColIndex + 1]);
06105         // store the border segment in the cell map and update cellBorders
06106         tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX,
06107                                       cellEndColIndex, 1, currentBorder.owner, currentBorder.width, startSeg);
06108         // update the affected borders of the cell, col, and table
06109         if (info.cell) {
06110           info.cell->SetBorderWidth(secondSide, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(secondSide)));
06111         }
06112         if (info.rightCol) {
06113           BCPixelSize half = BC_BORDER_LEFT_HALF(currentBorder.width);
06114           info.rightCol->SetRightBorderWidth(PR_MAX(half, info.rightCol->GetRightBorderWidth()));
06115         }
06116         propData->mRightBorderWidth = LimitBorderWidth(PR_MAX(propData->mRightBorderWidth, currentBorder.width));
06117         //get row continuous borders
06118         if (rowFrame) {
06119           currentBorder = CompareBorders(this, info.cg, info.rightCol, info.rg,
06120                                          rowFrame, nsnull, tableIsLTR, TABLE_EDGE,
06121                                          NS_SIDE_RIGHT, ADJACENT, t2p);
06122           rowFrame->SetContinuousBCBorderWidth(secondSide, currentBorder.width);
06123         }
06124       }
06125       //get row group continuous borders
06126       if (info.rgBottom && info.rg) { //once per rg, so check for bottom
06127         currentBorder = CompareBorders(this, info.cg, info.rightCol, info.rg, 
06128                                        nsnull, nsnull, tableIsLTR, TABLE_EDGE,
06129                                        NS_SIDE_RIGHT, ADJACENT, t2p);
06130         info.rg->SetContinuousBCBorderWidth(secondSide, currentBorder.width);
06131       }
06132     }
06133     else {
06134       PRInt32 segLength = 0;
06135       BCMapCellInfo priorAjaInfo;
06136       for (PRInt32 rowX = info.rowIndex; rowX <= cellEndRowIndex; rowX += segLength) {
06137         iter.PeekRight(info, rowX, ajaInfo);
06138         const nsIFrame* cg = (info.cgRight) ? info.cg : nsnull;
06139         currentBorder = CompareBorders(nsnull, cg, info.rightCol, nsnull, nsnull, info.cell,
06140                                        tableIsLTR, !TABLE_EDGE, NS_SIDE_RIGHT, ADJACENT, t2p);
06141         cg = (ajaInfo.cgLeft) ? ajaInfo.cg : nsnull;
06142         adjacentBorder = CompareBorders(nsnull, cg, ajaInfo.leftCol, nsnull, nsnull, ajaInfo.cell, 
06143                                         tableIsLTR, !TABLE_EDGE, NS_SIDE_LEFT, !ADJACENT, t2p);
06144         currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, !HORIZONTAL);
06145                           
06146         segLength = PR_MAX(1, ajaInfo.rowIndex + ajaInfo.rowSpan - rowX);
06147         segLength = PR_MIN(segLength, info.rowIndex + info.rowSpan - rowX);
06148 
06149         // update lastVerBorders and see if a new segment starts
06150         startSeg = SetBorder(currentBorder, lastVerBorders[cellEndColIndex + 1]);
06151         // store the border segment in the cell map and update cellBorders
06152         if (RIGHT_DAMAGED(cellEndColIndex) && TOP_DAMAGED(rowX) && BOTTOM_DAMAGED(rowX)) {
06153           tableCellMap->SetBCBorderEdge(NS_SIDE_RIGHT, *info.cellMap, iter.mRowGroupStart, rowX, 
06154                                         cellEndColIndex, segLength, currentBorder.owner, currentBorder.width, startSeg);
06155           // update the borders of the cells and cols affected 
06156           if (info.cell) {
06157             info.cell->SetBorderWidth(secondSide, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(secondSide)));
06158           }
06159           if (info.rightCol) {
06160             BCPixelSize half = BC_BORDER_LEFT_HALF(currentBorder.width);
06161             info.rightCol->SetRightBorderWidth(PR_MAX(half, info.rightCol->GetRightBorderWidth()));
06162           }
06163           if (ajaInfo.cell) {
06164             ajaInfo.cell->SetBorderWidth(firstSide, PR_MAX(currentBorder.width, ajaInfo.cell->GetBorderWidth(firstSide)));
06165           }
06166           if (ajaInfo.leftCol) {
06167             BCPixelSize half = BC_BORDER_RIGHT_HALF(currentBorder.width);
06168             ajaInfo.leftCol->SetLeftBorderWidth(PR_MAX(half, ajaInfo.leftCol->GetLeftBorderWidth()));
06169           }
06170         }
06171         // update the top right corner
06172         PRBool hitsSpanOnRight = (rowX > ajaInfo.rowIndex) && (rowX < ajaInfo.rowIndex + ajaInfo.rowSpan);
06173         BCCornerInfo* trCorner = ((0 == rowX) || hitsSpanOnRight) 
06174                                  ? &topCorners[cellEndColIndex + 1] : &bottomCorners[cellEndColIndex + 1]; 
06175         trCorner->Update(NS_SIDE_BOTTOM, currentBorder);
06176         // if this is not the first time through, consider the segment to the right
06177         if (rowX != info.rowIndex) {
06178           const nsIFrame* rg = (priorAjaInfo.rgBottom) ? priorAjaInfo.rg : nsnull;
06179           currentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, priorAjaInfo.bottomRow, priorAjaInfo.cell,
06180                                          tableIsLTR, !TABLE_EDGE, NS_SIDE_BOTTOM, ADJACENT, t2p);
06181           rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull;
06182           adjacentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell,
06183                                           tableIsLTR, !TABLE_EDGE, NS_SIDE_TOP, !ADJACENT, t2p);
06184           currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, HORIZONTAL);
06185           trCorner->Update(NS_SIDE_RIGHT, currentBorder);
06186         }
06187         // store the top right corner in the cell map 
06188         if (RIGHT_DAMAGED(cellEndColIndex) && TOP_DAMAGED(rowX)) {
06189           if (0 != rowX) {
06190             tableCellMap->SetBCBorderCorner(eTopRight, *info.cellMap, iter.mRowGroupStart, rowX, cellEndColIndex, 
06191                                             trCorner->ownerSide, trCorner->subWidth, trCorner->bevel);
06192           }
06193           // store any corners this cell spans together with the aja cell
06194           for (PRInt32 rX = rowX + 1; rX < rowX + segLength; rX++) {
06195             tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, rX, 
06196                                             cellEndColIndex, trCorner->ownerSide, trCorner->subWidth, PR_FALSE);
06197           }
06198         }
06199         // update bottom right corner, topCorners, bottomCorners
06200         hitsSpanOnRight = (rowX + segLength < ajaInfo.rowIndex + ajaInfo.rowSpan);
06201         BCCornerInfo& brCorner = (hitsSpanOnRight) ? topCorners[cellEndColIndex + 1] 
06202                                                    : bottomCorners[cellEndColIndex + 1];
06203         brCorner.Set(NS_SIDE_TOP, currentBorder);
06204         priorAjaInfo = ajaInfo;
06205       }
06206     }
06207     for (PRInt32 colX = info.colIndex + 1; colX <= cellEndColIndex; colX++) {
06208       lastVerBorders[colX].Reset(0,1);
06209     }
06210 
06211     // find the dominant border considering the cell's bottom border, adjacent cells and the table, row group, row
06212     if (numRows == cellEndRowIndex + 1) { // touches bottom edge of table
06213       if (!tableBorderReset[NS_SIDE_BOTTOM]) {
06214         propData->mBottomBorderWidth = 0;
06215         tableBorderReset[NS_SIDE_BOTTOM] = PR_TRUE;
06216       }
06217       for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX++) {
06218         nsIFrame* colFrame = GetColFrame(colX); if (!colFrame) ABORT0();
06219         nsIFrame* cgFrame = colFrame->GetParent(); if (!cgFrame) ABORT0();
06220         currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg, info.bottomRow, info.cell,
06221                                        tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM, ADJACENT, t2p);
06222         // update/store the bottom left & bottom right corners 
06223         BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
06224         blCorner.Update(NS_SIDE_RIGHT, currentBorder);
06225         tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,                
06226                                         colX, blCorner.ownerSide, blCorner.subWidth, blCorner.bevel); 
06227         BCCornerInfo& brCorner = bottomCorners[colX + 1]; // bottom right
06228         brCorner.Update(NS_SIDE_LEFT, currentBorder);
06229         if (numCols == colX + 1) { // lower right corner of the table
06230           tableCellMap->SetBCBorderCorner(eBottomRight, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,               
06231                                           colX, brCorner.ownerSide, brCorner.subWidth, brCorner.bevel, PR_TRUE);  
06232         }
06233         // update lastBottomBorder and see if a new segment starts
06234         startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
06235         if (!startSeg) { 
06236            // make sure that we did not compare apples to oranges i.e. the current border 
06237            // should be a continuation of the lastBottomBorder, as it is a bottom border 
06238            // add 1 to the cellEndRowIndex
06239            startSeg = (lastBottomBorder.rowIndex != cellEndRowIndex + 1);
06240         }
06241         // store the border segment in the cell map and update cellBorders
06242         tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, 
06243                                       colX, 1, currentBorder.owner, currentBorder.width, startSeg);
06244         // update the bottom borders of the cell, the bottom row, and the table 
06245         if (info.cell) {
06246           info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(NS_SIDE_BOTTOM)));
06247         }
06248         if (info.bottomRow) {
06249           BCPixelSize half = BC_BORDER_TOP_HALF(currentBorder.width);
06250           info.bottomRow->SetBottomBCBorderWidth(PR_MAX(half, info.bottomRow->GetBottomBCBorderWidth()));
06251         }
06252         propData->mBottomBorderWidth = LimitBorderWidth(PR_MAX(propData->mBottomBorderWidth, currentBorder.width));
06253         // update lastBottomBorders
06254         lastBottomBorder.rowIndex = cellEndRowIndex + 1;
06255         lastBottomBorder.rowSpan = info.rowSpan;
06256         lastBottomBorders[colX] = lastBottomBorder;
06257         //get col continuous border
06258         currentBorder = CompareBorders(this, cgFrame, colFrame, info.rg, info.bottomRow,
06259                                        nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM,
06260                                        ADJACENT, t2p);
06261         ((nsTableColFrame*)colFrame)->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM,
06262                                                                 currentBorder.width);
06263       }
06264       //get row group/col group continuous border
06265       if (info.rg) {
06266         currentBorder = CompareBorders(this, nsnull, nsnull, info.rg, info.bottomRow,
06267                                        nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM,
06268                                        ADJACENT, t2p);
06269         info.rg->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
06270       }
06271       if (info.cg) {
06272         currentBorder = CompareBorders(this, info.cg, nsnull, info.rg, info.bottomRow,
06273                                        nsnull, tableIsLTR, TABLE_EDGE, NS_SIDE_BOTTOM,
06274                                        ADJACENT, t2p);
06275         info.cg->SetContinuousBCBorderWidth(NS_SIDE_BOTTOM, currentBorder.width);
06276       }
06277     }
06278     else {
06279       PRInt32 segLength = 0;
06280       for (PRInt32 colX = info.colIndex; colX <= cellEndColIndex; colX += segLength) {
06281         iter.PeekBottom(info, colX, ajaInfo);
06282         const nsIFrame* rg = (info.rgBottom) ? info.rg : nsnull;
06283         currentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, info.bottomRow, info.cell, 
06284                                        tableIsLTR, !TABLE_EDGE, NS_SIDE_BOTTOM, ADJACENT, t2p);
06285         rg = (ajaInfo.rgTop) ? ajaInfo.rg : nsnull;
06286         adjacentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, ajaInfo.topRow, ajaInfo.cell, 
06287                                         tableIsLTR, !TABLE_EDGE, NS_SIDE_TOP, !ADJACENT, t2p);
06288         currentBorder = CompareBorders(!CELL_CORNER, currentBorder, adjacentBorder, HORIZONTAL);
06289         segLength = PR_MAX(1, ajaInfo.colIndex + ajaInfo.colSpan - colX);
06290         segLength = PR_MIN(segLength, info.colIndex + info.colSpan - colX);
06291 
06292         // update, store the bottom left corner
06293         BCCornerInfo& blCorner = bottomCorners[colX]; // bottom left
06294         PRBool hitsSpanBelow = (colX > ajaInfo.colIndex) && (colX < ajaInfo.colIndex + ajaInfo.colSpan);
06295         PRBool update = PR_TRUE;
06296         if ((colX == info.colIndex) && (colX > damageArea.x)) {
06297           PRInt32 prevRowIndex = lastBottomBorders[colX - 1].rowIndex;
06298           if (prevRowIndex > cellEndRowIndex + 1) { // hits a rowspan on the right
06299             update = PR_FALSE; // the corner was taken care of during the cell on the left
06300           }
06301           else if (prevRowIndex < cellEndRowIndex + 1) { // spans below the cell to the left
06302             topCorners[colX] = blCorner;
06303             blCorner.Set(NS_SIDE_RIGHT, currentBorder);
06304             update = PR_FALSE;
06305           }
06306         }
06307         if (update) {
06308           blCorner.Update(NS_SIDE_RIGHT, currentBorder);
06309         }
06310         if (BOTTOM_DAMAGED(cellEndRowIndex) && LEFT_DAMAGED(colX)) {
06311           if (hitsSpanBelow) {
06312             tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex, colX,
06313                                             blCorner.ownerSide, blCorner.subWidth, blCorner.bevel);
06314           }
06315           // store any corners this cell spans together with the aja cell
06316           for (PRInt32 cX = colX + 1; cX < colX + segLength; cX++) {
06317             BCCornerInfo& corner = bottomCorners[cX];
06318             corner.Set(NS_SIDE_RIGHT, currentBorder);
06319             tableCellMap->SetBCBorderCorner(eBottomLeft, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,
06320                                             cX, corner.ownerSide, corner.subWidth, PR_FALSE);
06321           }
06322         }
06323         // update lastBottomBorders and see if a new segment starts
06324         startSeg = SetHorBorder(currentBorder, blCorner, lastBottomBorder);
06325         if (!startSeg) { 
06326            // make sure that we did not compare apples to oranges i.e. the current border 
06327            // should be a continuation of the lastBottomBorder, as it is a bottom border 
06328            // add 1 to the cellEndRowIndex
06329            startSeg = (lastBottomBorder.rowIndex != cellEndRowIndex + 1);
06330         }
06331         lastBottomBorder.rowIndex = cellEndRowIndex + 1;
06332         lastBottomBorder.rowSpan = info.rowSpan;
06333         for (PRInt32 cX = colX; cX < colX + segLength; cX++) {
06334           lastBottomBorders[cX] = lastBottomBorder;
06335         }
06336 
06337         // store the border segment the cell map and update cellBorders
06338         if (BOTTOM_DAMAGED(cellEndRowIndex) && LEFT_DAMAGED(colX) && RIGHT_DAMAGED(colX)) {
06339           tableCellMap->SetBCBorderEdge(NS_SIDE_BOTTOM, *info.cellMap, iter.mRowGroupStart, cellEndRowIndex,
06340                                         colX, segLength, currentBorder.owner, currentBorder.width, startSeg);
06341           // update the borders of the affected cells and rows
06342           if (info.cell) {
06343             info.cell->SetBorderWidth(NS_SIDE_BOTTOM, PR_MAX(currentBorder.width, info.cell->GetBorderWidth(NS_SIDE_BOTTOM)));
06344           }
06345           if (info.bottomRow) {
06346             BCPixelSize half = BC_BORDER_TOP_HALF(currentBorder.width);
06347             info.bottomRow->SetBottomBCBorderWidth(PR_MAX(half, info.bottomRow->GetBottomBCBorderWidth()));
06348           }
06349           if (ajaInfo.cell) {
06350             ajaInfo.cell->SetBorderWidth(NS_SIDE_TOP, PR_MAX(currentBorder.width, ajaInfo.cell->GetBorderWidth(NS_SIDE_TOP)));
06351           }
06352           if (ajaInfo.topRow) {
06353             BCPixelSize half = BC_BORDER_BOTTOM_HALF(currentBorder.width);
06354             ajaInfo.topRow->SetTopBCBorderWidth(PR_MAX(half, ajaInfo.topRow->GetTopBCBorderWidth()));
06355           }
06356         }
06357         // update bottom right corner
06358         BCCornerInfo& brCorner = bottomCorners[colX + segLength];
06359         brCorner.Update(NS_SIDE_LEFT, currentBorder);
06360       }
06361       if (!gotRowBorder && 1 == info.rowSpan && (ajaInfo.topRow || info.rgBottom)) {
06362         //get continuous row/row group border
06363         //we need to check the row group's bottom border if this is
06364         //the last row in the row group, but only a cell with rowspan=1
06365         //will know whether *this* row is at the bottom
06366         const nsIFrame* rg = (info.rgBottom) ? info.rg : nsnull;
06367         currentBorder = CompareBorders(nsnull, nsnull, nsnull, rg, info.bottomRow,
06368                                        nsnull, tableIsLTR, !TABLE_EDGE, NS_SIDE_BOTTOM,
06369