/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "BasicTableLayoutStrategy.h"
#include "nsTableFrame.h"
#include "nsTableColFrame.h"
#include "nsTableCellFrame.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsVoidArray.h"
#include "nsIPtr.h"
#include "nsHTMLIIDs.h"

NS_DEF_PTR(nsIStyleContext);


#ifdef NS_DEBUG
static PRBool gsDebug = PR_FALSE;
#else
static const PRBool gsDebug = PR_FALSE;
#endif

const nscoord gBigSpace = 100000;

/* ---------- ProportionalColumnLayoutStruct ---------- */  
// TODO: make public so other subclasses can use it

/** useful info about a column for layout */
struct ProportionalColumnLayoutStruct
{
  ProportionalColumnLayoutStruct(PRInt32 aColIndex, 
                                 nscoord aMinColWidth, 
                                 nscoord aMaxColWidth, 
                                 PRInt32 aProportion)
  {
    mColIndex = aColIndex;
    mMinColWidth = aMinColWidth;
    mMaxColWidth = aMaxColWidth;
    mProportion = aProportion;
  };

  PRInt32 mColIndex;
  nscoord mMinColWidth; 
  nscoord mMaxColWidth; 
  PRInt32 mProportion;
};


/* ---------- ColSpanStruct ---------- */
struct ColSpanStruct
{
  PRInt32 colIndex;
  PRInt32 colSpan;
  nscoord width;

  ColSpanStruct(PRInt32 aSpan, PRInt32 aIndex, nscoord aWidth)
  {
    colSpan = aSpan;
    colIndex = aIndex;
    width = aWidth;
  }
};




/* ---------- BasicTableLayoutStrategy ---------- */

/* return true if the style indicates that the width is fixed 
 * for the purposes of column width determination
 */
inline
PRBool BasicTableLayoutStrategy::IsFixedWidth(const nsStylePosition* aStylePosition,
                                              const nsStyleTable* aStyleTable)
{
  return PRBool ((eStyleUnit_Coord==aStylePosition->mWidth.GetUnit()) ||
                 (eStyleUnit_Coord==aStyleTable->mSpanWidth.GetUnit()));
}


BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aFrame)
{
  NS_ASSERTION(nsnull!=aFrame, "bad frame arg");

  mTableFrame = aFrame;
  mMinTableWidth=0;
  mMaxTableWidth=0;
  mFixedTableWidth=0;
}

BasicTableLayoutStrategy::~BasicTableLayoutStrategy()
{
}

PRBool BasicTableLayoutStrategy::Initialize(nsSize* aMaxElementSize, PRInt32 aNumCols)
{
#ifdef NS_DEBUG
  nsIFrame *tablePIF=nsnull;
  mTableFrame->GetPrevInFlow(tablePIF);
  NS_ASSERTION(nsnull==tablePIF, "never ever call me on a continuing frame!");
#endif

  PRBool result = PR_TRUE;

  // re-init instance variables
  mNumCols = aNumCols;
  mMinTableWidth=0;
  mMaxTableWidth=0;
  mFixedTableWidth=0;
  mCols = mTableFrame->GetEffectiveCOLSAttribute();

  // Step 1 - assign the width of all fixed-width columns
  AssignPreliminaryColumnWidths();

  // set aMaxElementSize here because we compute mMinTableWidth in AssignPreliminaryColumnWidths
  if (nsnull!=aMaxElementSize)
  {
    SetMaxElementSize(aMaxElementSize);
  }

  return result;
}

void BasicTableLayoutStrategy::SetMaxElementSize(nsSize* aMaxElementSize)
{
  if (nsnull!=aMaxElementSize)
  {
    aMaxElementSize->height = 0;
    nsMargin borderPadding;
    const nsStylePosition* tablePosition;
    const nsStyleSpacing* tableSpacing;
    mTableFrame->GetStyleData(eStyleStruct_Position, ((const nsStyleStruct *&)tablePosition));
    mTableFrame->GetStyleData(eStyleStruct_Spacing , ((const nsStyleStruct *&)tableSpacing));
    tableSpacing->CalcBorderPaddingFor(mTableFrame, borderPadding);
    if (tablePosition->mWidth.GetUnit()==eStyleUnit_Coord)
    {
      aMaxElementSize->width = tablePosition->mWidth.GetCoordValue();
      aMaxElementSize->width = PR_MAX(aMaxElementSize->width, mMinTableWidth);
      //XXX: need to factor in borderpadding here!
    }   
    else
    {
      aMaxElementSize->width = mMinTableWidth + borderPadding.left + borderPadding.right;
    }


    if (PR_TRUE==gsDebug) 
      printf("%p BTLS::Init setting aMaxElementSize->width = %d\n", 
              mTableFrame, aMaxElementSize->width);
  }
}

PRBool BasicTableLayoutStrategy::BalanceColumnWidths(nsIStyleContext *aTableStyle,
                                                     const nsHTMLReflowState& aReflowState,
                                                     nscoord aMaxWidth)
{
#ifdef NS_DEBUG
  nsIFrame *tablePIF=nsnull;
  mTableFrame->GetPrevInFlow(tablePIF);
  NS_ASSERTION(nsnull==tablePIF, "never ever call me on a continuing frame!");
#endif

  PRBool result;

  NS_ASSERTION(nsnull!=aTableStyle, "bad arg");
  if (nsnull==aTableStyle)
    return PR_FALSE;


  nscoord specifiedTableWidth = 0; // not cached as a data member because it can vary depending on aMaxWidth
  PRBool tableIsAutoWidth = nsTableFrame::TableIsAutoWidth(mTableFrame, aTableStyle, aReflowState, specifiedTableWidth);
  // HACK!  Fix TableIsAutoWidth to return the right width
  if (specifiedTableWidth>aMaxWidth)
    specifiedTableWidth = aMaxWidth;
  if (NS_UNCONSTRAINEDSIZE==specifiedTableWidth)
  {
    specifiedTableWidth=0;
    tableIsAutoWidth=PR_TRUE;
  }

  // Step 2 - determine how much space is really available
  nscoord availWidth = aMaxWidth;                         // start with the max width I've been given
  if (NS_UNCONSTRAINEDSIZE!=availWidth)                   // if that's not infinite, subtract the fixed columns
    availWidth -= mFixedTableWidth;                       // that have already been accounted for
  if (PR_FALSE==tableIsAutoWidth)                         // if the table has a specified width
    availWidth = specifiedTableWidth - mFixedTableWidth;  // use it, minus the fixed columns already accounted for
  //if (0!=mMinTableWidth && mMinTableWidth>availWidth)     // if the computed available size is too small
    //availWidth = mMinTableWidth;                          // bump it up to the min
  if (0>availWidth)                                       // avail width can never be negative
    availWidth=0;

  // Step 3 - assign the width of all proportional-width columns in the remaining space
  if (PR_TRUE==gsDebug) 
  {
    printf ("BalanceColumnWidths with aMaxWidth = %d, availWidth = %d\n", aMaxWidth, availWidth);
    printf ("\t\t specifiedTW = %d, min/maxTW = %d %d\n", specifiedTableWidth, mMinTableWidth, mMaxTableWidth);
    printf ("\t\t reflow reason = %d\n", aReflowState.reason);
  }
  if (PR_TRUE==gsDebug)
  {
    printf("\n%p: BEGIN BALANCE COLUMN WIDTHS\n", mTableFrame);
    for (PRInt32 i=0; i<mNumCols; i++)
      printf("  col %d assigned width %d\n", i, mTableFrame->GetColumnWidth(i));
    printf("\n");
  }
  result = BalanceProportionalColumns(aReflowState, availWidth, aMaxWidth,
                                      specifiedTableWidth, tableIsAutoWidth);

  if (PR_TRUE==gsDebug)
  {
    printf("\n%p: END BALANCE COLUMN WIDTHS\n", mTableFrame);
    for (PRInt32 i=0; i<mNumCols; i++)
      printf("  col %d assigned width %d\n", i, mTableFrame->GetColumnWidth(i));
    printf("\n");
  }

#ifdef NS_DEBUG   // sanity check for table column widths
  for (PRInt32 i=0; i<mNumCols; i++)
  {
    nsTableColFrame *colFrame;
    mTableFrame->GetColumnFrame(i, colFrame);
    nscoord minColWidth = colFrame->GetMinColWidth();
    nscoord assignedColWidth = mTableFrame->GetColumnWidth(i);
    NS_ASSERTION(assignedColWidth >= minColWidth, "illegal width assignment");
  }
#endif

  return result;
}

// Step 1 - assign the width of all fixed-width columns, all other columns get there max, 
//          and calculate min/max table width
PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
{
  if (gsDebug==PR_TRUE) printf ("** %p: AssignPreliminaryColumnWidths **\n", mTableFrame);
  nsVoidArray *spanList=nsnull;
  nsVoidArray *colSpanList=nsnull;

  PRBool hasColsAttribute = (PRBool)(NS_STYLE_TABLE_COLS_NONE!=mCols);
  
  PRInt32 *minColWidthArray = nsnull;   // used for computing the effect of COLS attribute
  PRInt32 *maxColWidthArray = nsnull;   // used for computing the effect of COLS attribute
  if (PR_TRUE==hasColsAttribute)
  { 
    minColWidthArray = new PRInt32[mNumCols];
    maxColWidthArray = new PRInt32[mNumCols];
  }

  nscoord cellPadding=mTableFrame->GetCellPadding();
  if (gsDebug) printf ("table cell padding = %d\n", cellPadding);

  PRInt32 numRows = mTableFrame->GetRowCount();
  PRInt32 colIndex, rowIndex;

  // for every column, determine it's min and max width, and keep track of the table width
  for (colIndex = 0; colIndex<mNumCols; colIndex++)
  { 
    nscoord minColWidth = 0;              // min col width factoring in table attributes
    nscoord minColContentWidth = 0;       // min width of the col's contents, does not take into account table attributes
    nscoord maxColWidth = 0;              // max col width factoring in table attributes
    nscoord effectiveMinColumnWidth = 0;  // min col width ignoring cells with colspans
    nscoord effectiveMaxColumnWidth = 0;  // max col width ignoring cells with colspans
    nscoord specifiedFixedColWidth = 0;   /* the width of the column if given stylistically (or via cell Width attribute)
                                             only applicable if haveColWidth==PR_TRUE 
                                           */
    PRBool haveColWidth = PR_FALSE;       /* if true, the column has a width either from HTML width attribute,
                                             from a style rule on the column, 
                                             or from a width attr/style on a cell that has colspan==1 
                                           */
    // Get column information
    nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
    if (gsDebug) printf("BTLS::APCW - got colFrame %p for colIndex %d\n", colFrame, colIndex);
    NS_ASSERTION(nsnull!=colFrame, "bad col frame");

    // Get the columns's style
    const nsStylePosition* colPosition;
    colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
    const nsStyleTable* colTableStyle;
    colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);

    // Get fixed column width if it has one
    if (eStyleUnit_Coord==colPosition->mWidth.GetUnit()) 
    {
      haveColWidth = PR_TRUE;
      specifiedFixedColWidth = colPosition->mWidth.GetCoordValue();
      specifiedFixedColWidth += (cellPadding*2);
      if (gsDebug) printf("BTLS::APCW - got specified col width = %d\n", specifiedFixedColWidth);
    }

    /* Scan the column, simulatneously assigning column widths
     * and computing the min/max column widths
     */
    PRInt32 firstRowIndex = -1;
    PRInt32 maxColSpan = 1;
    PRBool cellGrantingWidth=PR_TRUE;
    for (rowIndex = 0; rowIndex<numRows; rowIndex++)
    {
      nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
      if (nsnull==cellFrame)
      { // there is no cell in this row that corresponds to this column
        continue;
      }
      if (-1==firstRowIndex)
        firstRowIndex = rowIndex;
      if (rowIndex!=cellFrame->GetRowIndex()) {
        // For cells that span rows, we only figure it in once
        NS_ASSERTION(1 != cellFrame->GetRowSpan(), "row index does not match row span");  // sanity check
        continue;
      }
      PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
      if (colSpan>maxColSpan)
        maxColSpan = colSpan;
      if (colIndex!=cellFrame->GetColIndex()) {
        // For cells that span cols, we figure in the row using previously-built SpanInfo 
        NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match col span");  // sanity check
        continue;
      }

      nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
      nsSize cellDesiredSize = cellFrame->GetPass1DesiredSize();
      nscoord cellDesiredWidth = cellDesiredSize.width;
      nscoord cellMinWidth = cellMinSize.width;

      if (1==colSpan)
      {
        if (0==minColContentWidth)
          minColContentWidth = cellMinWidth;
        else
          minColContentWidth = PR_MAX(minColContentWidth, cellMinWidth);
      }
      else
        minColContentWidth = PR_MAX(minColContentWidth, cellMinWidth/colSpan);  // no need to divide this proportionately
      if (gsDebug==PR_TRUE) 
        printf ("  for cell %d with colspan=%d, min = %d,%d  and  des = %d,%d\n", 
                rowIndex, colSpan, cellMinSize.width, cellMinSize.height,
                cellDesiredSize.width, cellDesiredSize.height);

      if (PR_TRUE==haveColWidth)
      {
        /* This col has a specified coord fixed width, so set the min and max width to the larger of 
           (specified width, largest max_element_size of the cells in the column)
           factoring in the min width of the prior cells (stored in minColWidth) */
        nscoord widthForThisCell = specifiedFixedColWidth;
        if (0==specifiedFixedColWidth) // set to min
          specifiedFixedColWidth = cellMinWidth;
        widthForThisCell = PR_MAX(widthForThisCell, effectiveMinColumnWidth);
        if (1==colSpan)
          widthForThisCell = PR_MAX(widthForThisCell, cellMinWidth);
        mTableFrame->SetColumnWidth(colIndex, widthForThisCell);
        maxColWidth = widthForThisCell;
        minColWidth = PR_MAX(minColWidth, cellMinWidth);
        if (1==colSpan) 
        {
          effectiveMaxColumnWidth = PR_MAX(effectiveMaxColumnWidth, widthForThisCell);
          if (0==effectiveMinColumnWidth)
            effectiveMinColumnWidth = cellMinWidth;//widthForThisCell; 
          else
            //effectiveMinColumnWidth = PR_MIN(effectiveMinColumnWidth, widthForThisCell);
          //above line works for most tables, but it can't be right
          effectiveMinColumnWidth = PR_MAX(effectiveMinColumnWidth, cellMinWidth);
          //above line seems right and works for xT1, but breaks lots of other tables.
        }
      }
      else
      {
        if (maxColWidth < cellDesiredWidth)
          maxColWidth = cellDesiredWidth;
        if ((1==colSpan) && (effectiveMaxColumnWidth < cellDesiredWidth))
          effectiveMaxColumnWidth = cellDesiredWidth;
        if (minColWidth < cellMinWidth)
          minColWidth = cellMinWidth;
        // effectiveMinColumnWidth is the min width as if no cells with colspans existed
        if ((1==colSpan) && (effectiveMinColumnWidth < cellMinWidth))
          effectiveMinColumnWidth = cellMinWidth;
      }

      if (1<colSpan)
      {
        // add the column to our list of post-process columns, if all of the intersected columns are auto
        PRBool okToAdd = PR_TRUE;
        if (numRows==1 || (PR_FALSE==IsFixedWidth(colPosition, colTableStyle)))
          okToAdd = PR_FALSE;
        else
        {
          for (PRInt32 i=1; i<colSpan; i++)
          {
            nsTableColFrame *cf;
            mTableFrame->GetColumnFrame(i+colIndex, cf);
            const nsStylePosition* colPos;
            cf->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPos);
            if (colPos->mWidth.GetUnit() != eStyleUnit_Auto &&
               (nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN!=cf->GetWidthSource()))
            {
              okToAdd = PR_FALSE;
              break;
            }
          }
        }

        nscoord width = cellDesiredSize.width;  // used below as the cell's "natural" width
        if (eStyleUnit_Coord==colTableStyle->mSpanWidth.GetUnit())
          width = colSpan*colTableStyle->mSpanWidth.GetCoordValue();  // "colSpan*" because table frame divided per column spanned

        if (PR_TRUE==okToAdd)
        {
          nscoord colwidth=PR_MAX(width, cellMinWidth);
          ColSpanStruct *colSpanInfo = new ColSpanStruct(colSpan, colIndex, colwidth);
          if (nsnull==colSpanList)
            colSpanList = new nsVoidArray();
          colSpanList->AppendElement(colSpanInfo);
        }

        if (PR_TRUE==cellGrantingWidth)
        {
          // add the cell to our list of spanning cells
          SpanInfo *spanInfo = new SpanInfo(colIndex, colSpan, cellMinWidth, width);
          if (nsnull==spanList)
            spanList = new nsVoidArray();
          spanList->AppendElement(spanInfo);
        }
      }

      //bookkeeping:  is this the cell that gave the column it's fixed width attribute?
      // must be done after "haveColWidth && cellGrantingWidth" used above
      /* since we want the biggest, I don't think this is valid anymore
         it made sense when the rule was to grab the first cell that contributed a width
       */
      /*
      if (PR_TRUE==cellGrantingWidth)
      {
        const nsStylePosition* cellPosition;
        cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)cellPosition);
        if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit())
          cellGrantingWidth=PR_FALSE; //I've found the cell that gave the col it's width
      } 
      */

      // book 'em, Danno
      if (gsDebug) {
        printf ("    after cell %d, minColWidth = %d and maxColWidth = %d\n",
                rowIndex, minColWidth, maxColWidth);
      }
    } // end rowIndex for loop

    // adjust the "fixed" width for content that is too wide
    if (effectiveMinColumnWidth>specifiedFixedColWidth)
      specifiedFixedColWidth=effectiveMinColumnWidth;
    // do all the global bookkeeping, factoring in margins
    nscoord colInset = mTableFrame->GetCellSpacingX();
    // keep a running total of the amount of space taken up by all fixed-width columns
    if ((PR_TRUE==haveColWidth) && (nsTableColFrame::eWIDTH_SOURCE_CELL==colFrame->GetWidthSource()))
    {
      mFixedTableWidth += specifiedFixedColWidth + colInset;
      if (0==colIndex)
        mFixedTableWidth += colInset;
      if (PR_TRUE==gsDebug) printf("setting mFixedTableWidth=%d\n", mFixedTableWidth);
    }

    // cache the computed column info
    colFrame->SetMinColWidth(effectiveMinColumnWidth);
    colFrame->SetMaxColWidth(effectiveMaxColumnWidth);
    colFrame->SetEffectiveMinColWidth(effectiveMinColumnWidth);
    colFrame->SetEffectiveMaxColWidth(effectiveMaxColumnWidth);
    // this is the default, the real adjustment happens below where we deal with colspans
    colFrame->SetAdjustedMinColWidth(effectiveMinColumnWidth);  
    if ((PR_TRUE==haveColWidth) && 
        (nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN!=colFrame->GetWidthSource()))
      mTableFrame->SetColumnWidth(colIndex, specifiedFixedColWidth);
    else
      mTableFrame->SetColumnWidth(colIndex, effectiveMaxColumnWidth);
    if (gsDebug)
      printf ("col %d, set col width to = %d\n", colIndex, mTableFrame->GetColumnWidth(colIndex));

    if (PR_TRUE==hasColsAttribute)
    {
      minColWidthArray[colIndex] = minColWidth;
      maxColWidthArray[colIndex] = maxColWidth;
    }
    if (gsDebug==PR_TRUE) 
      printf ("after col %d, minColWidth=%d effectiveMinColumnWidth=%d\n\teffectiveMaxColumnWidth = %d\n", 
              colIndex, minColWidth, effectiveMinColumnWidth, effectiveMaxColumnWidth);
  }

  // now, post-process the computed values based on the table attributes

  // first, factor in max values of spanning cells proportionately.
  // We can't do this above in the first loop, because we don't have enough information
  // to determine the proportion each column gets from spanners.
  if (nsnull!=spanList)
  {
    // we only want to do this if there are auto-cells involved
    // or if we have columns that are provisionally fixed-width with colspans
    PRInt32 numAutoColumns=0;
    PRInt32 *autoColumns=nsnull;
    mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
    // for every column, handle spanning cells that impact that column
    for (PRInt32 colIndex=0; colIndex<mNumCols; colIndex++)
    {
      if (gsDebug) printf("handling span for %d\n", colIndex);
      PRInt32 spanCount = spanList->Count();
      // go through the list backwards so we can delete easily
      for (PRInt32 spanIndex=spanCount-1; 0<=spanIndex; spanIndex--)
      { // get each spanInfo struct and see if it impacts this column
        SpanInfo *spanInfo = (SpanInfo *)(spanList->ElementAt(spanIndex));
        // if the spanInfo is about a column before the current column, it effects 
        // the current column (otherwise it would have already been deleted.)
        if (spanInfo->initialColIndex <= colIndex)
        {
          if (-1==spanInfo->effectiveMaxWidthOfSpannedCols)
          { // if we have not yet computed effectiveMaxWidthOfSpannedCols, do it now
            // first, initialize the sums
            spanInfo->effectiveMaxWidthOfSpannedCols=0;
            spanInfo->effectiveMinWidthOfSpannedCols=0;
            // then compute the sums
            for (PRInt32 span=0; span<spanInfo->initialColSpan; span++)
            {
              nsTableColFrame *nextColFrame = mTableFrame->GetColFrame(colIndex+span);
              if (nsnull==nextColFrame)
                break;
              spanInfo->effectiveMaxWidthOfSpannedCols += nextColFrame->GetEffectiveMaxColWidth();
              spanInfo->effectiveMinWidthOfSpannedCols += nextColFrame->GetEffectiveMinColWidth();
            }
            if (gsDebug) printf("effective min total = %d, max total = %d\n", 
                          spanInfo->effectiveMinWidthOfSpannedCols, spanInfo->effectiveMaxWidthOfSpannedCols);
          }
          nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
          nscoord colMinWidth = colFrame->GetMinColWidth();
          
          // compute the spanning cell's contribution to the column min width
          // this is the "adjusted" column width, used in SetTableToMinWidth
          nscoord spanCellMinWidth;
          if (0!=spanInfo->effectiveMinWidthOfSpannedCols)
          {
            float percent = ((float)(colFrame->GetEffectiveMinColWidth())) /
                              ((float)(spanInfo->effectiveMinWidthOfSpannedCols));
            spanCellMinWidth = NSToCoordRound(((float)(spanInfo->cellMinWidth)) * percent);
            if (gsDebug==PR_TRUE) 
              printf ("spanCellMinWidth portion = %d from percent=%f, cellMW=%d, effMinColW=%d, sum=%d\n", 
                       spanCellMinWidth, percent, spanInfo->cellMinWidth, colFrame->GetEffectiveMinColWidth(),
                       spanInfo->effectiveMinWidthOfSpannedCols);
            if (colMinWidth < spanCellMinWidth)
              colFrame->SetAdjustedMinColWidth(spanCellMinWidth);     // set the new min width for the col
          }
          else
          {
            spanCellMinWidth = spanInfo->cellMinWidth/spanInfo->initialColSpan;
            if (colMinWidth < spanCellMinWidth)
            {
              colFrame->SetAdjustedMinColWidth(spanCellMinWidth);
            }
          }

          // compute the spanning cell's contribution to the column max width
          nscoord colMaxWidth = colFrame->GetMaxColWidth(); 
          nscoord spanCellMaxWidth;
          if (0!=spanInfo->effectiveMaxWidthOfSpannedCols)
          {
            float percent = ((float)(colFrame->GetEffectiveMaxColWidth())) /
                               ((float)(spanInfo->effectiveMaxWidthOfSpannedCols));
            spanCellMaxWidth = NSToCoordRound(((float)(spanInfo->cellDesiredWidth)) * percent);
            if (gsDebug==PR_TRUE) 
              printf ("spanCellMaxWidth portion = %d with percent = %f from cellDW = %d, effMaxColW=%d and sum=%d\n", 
                      spanCellMaxWidth, percent, spanInfo->cellDesiredWidth, colFrame->GetEffectiveMaxColWidth(),
                      spanInfo->effectiveMaxWidthOfSpannedCols);
            if (colMaxWidth < spanCellMaxWidth)
            {
              // make sure we're at least as big as our min
              spanCellMaxWidth = PR_MAX(spanCellMaxWidth, colMinWidth);
              colFrame->SetMaxColWidth(spanCellMaxWidth);     // set the new max width for the col
              mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);  // set the column to the new desired max width
              if (gsDebug==PR_TRUE) 
              {
                printf ("for spanning cell into col %d with remaining span=%d, old max = %d, new max = %d\n", 
                        colIndex, spanInfo->span, colMaxWidth, spanCellMaxWidth);
              }
            }
          }
          else
          {
            spanCellMaxWidth = spanInfo->cellDesiredWidth/spanInfo->initialColSpan;
            nscoord minColWidth = colFrame->GetMinColWidth();
            spanCellMaxWidth = PR_MAX(spanCellMaxWidth, minColWidth);
            colFrame->SetMaxColWidth(spanCellMaxWidth);
            mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);
            if (gsDebug==PR_TRUE) 
              printf ("    for spanning cell into col %d with remaining span=%d, old max = %d, new max = %d\n", 
                      colIndex, spanInfo->span, colMaxWidth, spanCellMaxWidth);
          }

          spanInfo->span--;
          if (0==spanInfo->span)
          {
            spanList->RemoveElementAt(spanIndex);
            delete spanInfo;
          }

          // begin code that respects column width attribute
          /* the code below checks to see if the column has a width attribute (either it's own or a cell's)
           * if so, if it fits within the constraints we computed above, we use it.
           * why go through all that pain above, then?  because the given width attribute might not
           * be rational.  So we need to act as if it doesn't exist and then fit it in if it makes sense.
           * a shortcut that checks for a column width attribute and skips the above computations
           * would not work without lots of extra computation that would lead you down the same path anyway.
           */
          const nsStylePosition* colPosition;
          colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
          // Get fixed column width if it has one
          if (eStyleUnit_Coord==colPosition->mWidth.GetUnit()) 
          {
            if (nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN!=colFrame->GetWidthSource())
            {
              nscoord specifiedFixedColWidth = colPosition->mWidth.GetCoordValue();
              specifiedFixedColWidth += (cellPadding*2);
              if (specifiedFixedColWidth>=colFrame->GetEffectiveMinColWidth())
              {
                mTableFrame->SetColumnWidth(colIndex, specifiedFixedColWidth);
                colFrame->SetMaxColWidth(specifiedFixedColWidth);
              }
            }
          }
          // end code that respects column width attribute
        }
      }
    }
  }

  // now set the min and max table widths
  SetMinAndMaxTableWidths();

  // then, handle the COLS attribute (equal column widths)
  // if there is a COLS attribute, fix up mMinTableWidth and mMaxTableWidth
  if (PR_TRUE==hasColsAttribute)
  {
    if (gsDebug) printf("has COLS attribute = %d\n", mCols);
    // for every effected column, subtract out its prior contribution and add back in the new value
    PRInt32 numColsEffected = mNumCols;
    if (NS_STYLE_TABLE_COLS_ALL!=mCols)
      numColsEffected = mCols;
    PRInt32 maxOfMinColWidths=0;
    PRInt32 maxOfMaxColWidths=0;
    PRInt32 effectedColIndex;
    for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
    {
      if (maxOfMinColWidths < minColWidthArray[effectedColIndex])
        maxOfMinColWidths = minColWidthArray[effectedColIndex];
      if (maxOfMaxColWidths < maxColWidthArray[effectedColIndex])
        maxOfMaxColWidths = maxColWidthArray[effectedColIndex];
    }
    for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
    {
      // subtract out the prior contributions of this column
      // and add back in the adjusted value
      if (NS_UNCONSTRAINEDSIZE!=mMinTableWidth)
      {
        mMinTableWidth -= minColWidthArray[effectedColIndex];
        mMinTableWidth += maxOfMinColWidths;
      }
      if (NS_UNCONSTRAINEDSIZE!=mMaxTableWidth)
      {
        mMaxTableWidth -= maxColWidthArray[effectedColIndex];
        mMaxTableWidth += maxOfMaxColWidths;
      }
      nsTableColFrame *colFrame;
      mTableFrame->GetColumnFrame(effectedColIndex, colFrame);
      colFrame->SetMaxColWidth(maxOfMaxColWidths);  // cache the new column max width (min width is uneffected)
      colFrame->SetEffectiveMaxColWidth(maxOfMaxColWidths);
      if (gsDebug) printf ("col %d now has max col width %d\n", effectedColIndex, maxOfMaxColWidths);
    }
    delete [] minColWidthArray;
    delete [] maxColWidthArray;
  }
  if (nsnull!=colSpanList)
    DistributeFixedSpace(colSpanList);

  if (PR_TRUE==gsDebug)
    printf ("%p: aMinTW=%d, aMaxTW=%d\n", mTableFrame, mMinTableWidth, mMaxTableWidth);

  // clean up
  if (nsnull!=spanList)
  {
    if (gsDebug) printf("BTLS::APCW...space leak, span list not empty\n");
    delete spanList;
  }
  if (nsnull!=colSpanList)
  {
    PRInt32 colSpanListCount = colSpanList->Count();
    for (PRInt32 i=0; i<colSpanListCount; i++)
    {
      ColSpanStruct * colSpanInfo = (ColSpanStruct *)(colSpanList->ElementAt(i));
      if (nsnull!=colSpanInfo)
        delete colSpanInfo;
    }
    delete colSpanList;
  }
  return PR_TRUE;
}

void BasicTableLayoutStrategy::SetMinAndMaxTableWidths()
{
  if (gsDebug) printf("SetMinAndMaxTableWidths\n");
  PRInt32 colIndex, rowIndex;
  PRInt32 numRows = mTableFrame->GetRowCount();
  nscoord colInset = mTableFrame->GetCellSpacingX();
  for (rowIndex = 0; rowIndex<numRows; rowIndex++)
  {
    if (gsDebug) printf("  row %d\n", rowIndex);
    nscoord rowMinWidth = colInset;
    nscoord rowMaxWidth = colInset;
    for (colIndex = 0; colIndex<mNumCols; colIndex++)
    {
      if (gsDebug) printf("    col %d\n", colIndex);
      nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
      rowMinWidth += colInset;
      rowMaxWidth += colInset;
      if (nsnull==cellFrame)
      { // there is no cell in this row that corresponds to this column
        if (gsDebug) printf("    col %d skipped because there is no cell\n", colIndex);
        continue;
      }
      if (colIndex!=cellFrame->GetColIndex()) {
        // For cells that span cols, we figured in the cell the first time we saw it
        if (gsDebug) printf("    col %d skipped because it has colspan so we've already added it in\n", colIndex);
        continue;
      }
      nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
      nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
      nscoord cellMinWidth = PR_MAX(cellMinSize.width, colFrame->GetEffectiveMinColWidth());
      nsSize cellMaxSize = cellFrame->GetPass1DesiredSize();
      nscoord cellMaxWidth = PR_MAX(cellMaxSize.width, colFrame->GetEffectiveMaxColWidth());
      PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
      nscoord spanningCellMinWidth = (colSpan-1)*colInset; 
      if (gsDebug) printf("    cellMin=%d, cellMax=%d, spanningCellMin=%d\n", 
                          cellMinWidth, cellMaxWidth, spanningCellMinWidth);
      // spanning cells must be at least as wide as the columns they span, including the cell spacing spanned.
      if (NS_UNCONSTRAINEDSIZE!=rowMinWidth)
        rowMinWidth += PR_MAX(cellMinWidth, spanningCellMinWidth);
      if (NS_UNCONSTRAINEDSIZE!=rowMaxWidth)
        rowMaxWidth += PR_MAX(cellMaxWidth, spanningCellMinWidth);
      if (gsDebug) printf("    rowMinWidth=%d, rowMaxWidth=%d\n", rowMinWidth, rowMaxWidth);
    }
    if (gsDebug) printf("  rowMinWidth=%d, rowMaxWidth=%d\n", rowMinWidth, rowMaxWidth);
    // the largest row widths are the table widths
    mMinTableWidth = PR_MAX(mMinTableWidth, rowMinWidth);
    mMaxTableWidth = PR_MAX(mMaxTableWidth, rowMaxWidth);
    if (gsDebug) printf("  mMinTableWidth=%d, mMaxTableWidth=%d\n", mMinTableWidth, mMaxTableWidth);
  }
  // verify max of min row widths vs. sum of adjusted column min widths.  bigger one wins
  nscoord sumOfAdjustedColMinWidths=colInset;
  for (colIndex = 0; colIndex<mNumCols; colIndex++)
  {
    nsTableColFrame *colFrame;
    mTableFrame->GetColumnFrame(colIndex, colFrame);
    sumOfAdjustedColMinWidths += colFrame->GetAdjustedMinColWidth() + colInset;
    if (gsDebug) printf("  col %d has amcw=%d, cellspacing=%d, sum=%d\n", 
                        colIndex, colFrame->GetAdjustedMinColWidth(), colInset, sumOfAdjustedColMinWidths);
  }
  if (gsDebug) printf("  sumOfAdjustedColMinWidths=%d\n", sumOfAdjustedColMinWidths);
  /*
  mMinTableWidth = PR_MAX(mMinTableWidth, sumOfAdjustedColMinWidths);
  mMaxTableWidth = PR_MAX(mMinTableWidth, mMaxTableWidth);
  */
  if (gsDebug) printf("end SetMinAndMaxTW: minTW=%d, maxTW=%d with DMCW=%d\n", mMinTableWidth, mMaxTableWidth, sumOfAdjustedColMinWidths);
}

// take the fixed space spanned by the columns in aColSpanList 
// and distribute it proportionately (based on desired width)
void BasicTableLayoutStrategy::DistributeFixedSpace(nsVoidArray *aColSpanList)
{
  nscoord excess = 0;
  if (PR_TRUE==gsDebug) printf ("** DistributeFixedSpace:\n");
  // for all fixed-width columns, determine the amount of the specified width each column spanned recieves
  PRInt32 numSpanningCells = aColSpanList->Count();
  for (PRInt32 nextSpanningCell=0; nextSpanningCell<numSpanningCells; nextSpanningCell++)
  { // proportionately distributed extra space, based on the column's fixed width
    ColSpanStruct * colInfo = (ColSpanStruct *)aColSpanList->ElementAt(nextSpanningCell);
    PRInt32 colIndex = colInfo->colIndex;  
    PRInt32 colSpan = colInfo->colSpan;  
    nscoord totalColWidth = colInfo->width;
    
    // 1. get the sum of the effective widths of the columns in the span
    nscoord totalEffectiveWidth=0;
    nsTableColFrame * colFrame;
    PRInt32 i;
    for (i = 0; i<colSpan; i++)
    {
      mTableFrame->GetColumnFrame(colIndex+i, colFrame);
      totalEffectiveWidth += colFrame->GetColWidthForComputation();
    }

    // 2. next, compute the proportion to be added to each column, and add it
    for (i = 0; i<colSpan; i++)
    {
      mTableFrame->GetColumnFrame(colIndex+i, colFrame);
      float percent;
      percent = ((float)(colFrame->GetColWidthForComputation()))/((float)totalEffectiveWidth);
      nscoord newColWidth = NSToCoordRound(((float)totalColWidth)*percent);
      nscoord minColWidth = colFrame->GetEffectiveMinColWidth();
      nscoord oldColWidth = mTableFrame->GetColumnWidth(colIndex+i);
      if (newColWidth>minColWidth)
      {
        if (gsDebug==PR_TRUE) 
        {
          printf("  assigning fixed col width for spanning cells:  column %d set to %d\n", 
                 colIndex+i, newColWidth);
          printf("  minCW = %d oldCW = %d\n", minColWidth, oldColWidth);
        }
        mTableFrame->SetColumnWidth(colIndex+i, newColWidth);
        colFrame->SetEffectiveMaxColWidth(newColWidth);
      }
    }
  }
}

/* assign column widths to all non-fixed-width columns (adjusting fixed-width columns if absolutely necessary)
 aAvailWidth is the amount of space left in the table to distribute after fixed-width columns are accounted for
 aMaxWidth is the space the parent gave us (minus border & padding) to fit ourselves into
 aTableIsAutoWidth is true if the table is auto-width, false if it is anything else (percent, fixed, etc)
 */
PRBool BasicTableLayoutStrategy::BalanceProportionalColumns(const nsHTMLReflowState& aReflowState,
                                                            nscoord aAvailWidth,
                                                            nscoord aMaxWidth,
                                                            nscoord aTableSpecifiedWidth,
                                                            PRBool  aTableIsAutoWidth)
{
  PRBool result = PR_TRUE;

  nscoord actualMaxWidth; // the real target width, depends on if we're auto or specified width
  if (PR_TRUE==aTableIsAutoWidth)
    actualMaxWidth = aMaxWidth;
  else
    actualMaxWidth = PR_MIN(aMaxWidth, aTableSpecifiedWidth);

  if (NS_UNCONSTRAINEDSIZE==aMaxWidth  ||  NS_UNCONSTRAINEDSIZE==mMinTableWidth)
  { // the max width of the table fits comfortably in the available space
    if (gsDebug) printf ("  * table laying out in NS_UNCONSTRAINEDSIZE, calling BalanceColumnsTableFits\n");
    nscoord bigSpace = gBigSpace;
    bigSpace = PR_MAX(bigSpace, mMaxTableWidth);
    result = BalanceColumnsTableFits(aReflowState, bigSpace, 
                                     bigSpace, aTableSpecifiedWidth, aTableIsAutoWidth);
  }
  else if (mMinTableWidth > actualMaxWidth)
  { // the table doesn't fit in the available space
    if (gsDebug) printf ("  * table minTW does not fit, calling BalanceColumnsTableDoesNotFit\n");
    result = BalanceColumnsTableDoesNotFit();
  }
  else if (mMaxTableWidth <= actualMaxWidth)
  { // the max width of the table fits comfortably in the available space
    if (gsDebug) printf ("  * table desired size fits, calling BalanceColumnsTableFits\n");
    result = BalanceColumnsTableFits(aReflowState, aAvailWidth, 
                                     aMaxWidth, aTableSpecifiedWidth, aTableIsAutoWidth);
  }
  else
  { // the table fits somewhere between its min and desired size
    if (gsDebug) printf ("  * table desired size does not fit, calling BalanceColumnsConstrained\n");
    result = BalanceColumnsConstrained(aReflowState, aAvailWidth,
                                       actualMaxWidth, aTableIsAutoWidth);
  }

  return result;
}

// the table doesn't fit, so squeeze every column down to its minimum
PRBool BasicTableLayoutStrategy::BalanceColumnsTableDoesNotFit()
{
  PRBool result = PR_TRUE;

  PRBool hasColsAttribute = (PRBool)(NS_STYLE_TABLE_COLS_NONE!=mCols);
  
  PRInt32 *minColWidthArray = nsnull;
  if (PR_TRUE==hasColsAttribute)
  {
    minColWidthArray = new PRInt32[mNumCols];
  }

  PRInt32 numRows = mTableFrame->GetRowCount();
  for (PRInt32 colIndex = 0; colIndex<mNumCols; colIndex++)
  { 
    nscoord minAdjustedColWidth = 0;
    if (gsDebug==PR_TRUE) printf ("  for col %d\n", colIndex);

    nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
    NS_ASSERTION(nsnull!=colFrame, "bad col frame");

    // Get the columns's style
    const nsStylePosition* colPosition;
    colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
    const nsStyleTable* colTableStyle;
    colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);
    //if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
    {
      minAdjustedColWidth = colFrame->GetAdjustedMinColWidth();
      mTableFrame->SetColumnWidth(colIndex, minAdjustedColWidth);
      if (PR_TRUE==hasColsAttribute)
      {
        minColWidthArray[colIndex] = minAdjustedColWidth;
      }
    }
    if (gsDebug==PR_TRUE) 
      printf ("  2: col %d, set to width = %d\n", colIndex, mTableFrame->GetColumnWidth(colIndex));
  }

  // now, post-process the computed values based on the table attributes
  // if there is a COLS attribute, fix up mMinTableWidth and mMaxTableWidth
  if (PR_TRUE==hasColsAttribute)
  { // for every effected column, subtract out its prior contribution and add back in the new value
    PRInt32 numColsEffected = mNumCols;
    if (NS_STYLE_TABLE_COLS_ALL!=mCols)
      numColsEffected = mCols;
    nscoord maxOfEffectedColWidths=0;
    PRInt32 effectedColIndex;
    // XXX need to fix this and all similar code if any fixed-width columns intersect COLS
    for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
    {
      if (maxOfEffectedColWidths < minColWidthArray[effectedColIndex])
        maxOfEffectedColWidths = minColWidthArray[effectedColIndex];
    }
    for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
    {
      // set each effected column to the size of the largest column in the group
      mTableFrame->SetColumnWidth(effectedColIndex, maxOfEffectedColWidths);
      if (PR_TRUE==gsDebug)
        printf("  2 (cols): setting %d to %d\n", effectedColIndex, maxOfEffectedColWidths); 
    }
    // we're guaranteed here that minColWidthArray has been allocated, and that
    // if we don't get here, it was never allocated
    delete [] minColWidthArray;
  }
  return result;
}

/* the table fits in the given space.  Set all columns to their desired width,
 * and if we are not an auto-width table add extra space to fluff out the total width
 */
PRBool BasicTableLayoutStrategy::BalanceColumnsTableFits(const nsHTMLReflowState& aReflowState,
                                                         nscoord aAvailWidth,
                                                         nscoord aMaxWidth,
                                                         nscoord aTableSpecifiedWidth,
                                                         PRBool  aTableIsAutoWidth)
{
  PRBool  result = PR_TRUE;
  nscoord tableWidth=0;               // the width of the table as a result of setting column widths
  nscoord widthOfFixedTableColumns=0; // the sum of the width of all fixed-width columns plus margins
                                      // tableWidth - widthOfFixedTableColumns is the width of columns computed in this method
  PRInt32 totalSlices=0;              // the total number of slices the proportional-width columns request
  nsVoidArray *proportionalColumnsList=nsnull; // a list of the columns that are proportional-width
  nsVoidArray *spanList=nsnull;       // a list of the cells that span columns
  PRInt32 numRows = mTableFrame->GetRowCount();
  nscoord colInset = mTableFrame->GetCellSpacingX();
  PRInt32 colIndex;

  for (colIndex = 0; colIndex<mNumCols; colIndex++)
  { 
    if (gsDebug==PR_TRUE) printf ("for col %d\n", colIndex);
    // Get column information
    nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
    NS_ASSERTION(nsnull!=colFrame, "bad col frame");
    PRInt32 minColWidth = colFrame->GetMinColWidth();
    PRInt32 maxColWidth = 0;
    PRInt32 rowIndex;
    PRInt32 firstRowIndex = -1;

    const nsStylePosition* colPosition;
    colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);    
    const nsStyleTable* colTableStyle;
    colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);

    // first, deal with any cells that span into this column from a pervious column
      // go through the list backwards so we can delete easily
    if (nsnull!=spanList)
    {
      PRInt32 spanCount = spanList->Count();
      // go through the list backwards so we can delete easily
      for (PRInt32 spanIndex=spanCount-1; 0<=spanIndex; spanIndex--)
      {
        SpanInfo *spanInfo = (SpanInfo *)(spanList->ElementAt(spanIndex));
        if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
        {
          // compute the spanning cell's contribution to the column min width
          nscoord spanCellMinWidth;
          PRBool needsExtraMinWidth = PR_FALSE;
          /*
          if (spanInfo->effectiveMinWidthOfSpannedCols<spanInfo->cellMinWidth)
            needsExtraMinWidth = PR_TRUE;
            */
          if (PR_TRUE==needsExtraMinWidth)
          {
            if (0!=spanInfo->effectiveMinWidthOfSpannedCols)
            {
              spanCellMinWidth = (spanInfo->cellMinWidth * colFrame->GetEffectiveMinColWidth()) /
                                 (spanInfo->effectiveMinWidthOfSpannedCols);
              if (gsDebug==PR_TRUE) 
                printf ("    spanlist min: %d of %d\n", spanCellMinWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
              if (minColWidth < spanCellMinWidth)
              {
                minColWidth = spanCellMinWidth;     // set the new min width for the col
                if (gsDebug==PR_TRUE) 
                  printf ("    for spanning cell into col %d with remaining span=%d, new min = %d\n", 
                          colIndex, spanInfo->span, minColWidth);
                // if the new min width is greater than the desired width, bump up the desired width
                maxColWidth = PR_MAX(maxColWidth, minColWidth);
                if (gsDebug)
                  printf ("   and maxColWidth = %d\n", maxColWidth);
              }
            }
            else
            {
              spanCellMinWidth = spanInfo->cellMinWidth/spanInfo->initialColSpan;
              if (minColWidth < spanCellMinWidth)
              {
                minColWidth = spanCellMinWidth;
                if (gsDebug==PR_TRUE) 
                    printf ("    for spanning cell into col %d with remaining span=%d, new min = %d\n", 
                            colIndex, spanInfo->span, minColWidth);
                // if the new min width is greater than the desired width, bump up the desired width
                maxColWidth = PR_MAX(maxColWidth, minColWidth);
                if (gsDebug)
                    printf ("   and maxColWidth = %d\n", maxColWidth);
              }
            }
          }


          // compute the spanning cell's contribution to the column max width
          nscoord spanCellMaxWidth;
          if (0!=spanInfo->effectiveMaxWidthOfSpannedCols)
          {
            spanCellMaxWidth = (spanInfo->cellDesiredWidth * colFrame->GetEffectiveMaxColWidth()) /
                               (spanInfo->effectiveMaxWidthOfSpannedCols);
            if (gsDebug==PR_TRUE) 
              printf ("    spanlist max: %d of %d\n", spanCellMaxWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
            if (maxColWidth < spanCellMaxWidth)
            {
              spanCellMaxWidth = PR_MAX(spanCellMaxWidth, minColWidth);
              maxColWidth = spanCellMaxWidth;                 // set the new max width for the col
              mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);  // set the column to the new desired max width
              if (gsDebug==PR_TRUE) 
                printf ("    for spanning cell into col %d with remaining span=%d, \
                        new max = %d\n", 
                        colIndex, spanInfo->span, maxColWidth);
            }
          }
          else
          {
            spanCellMaxWidth = spanInfo->cellDesiredWidth/spanInfo->initialColSpan;
            maxColWidth = spanCellMaxWidth;
            mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);
            if (gsDebug==PR_TRUE) 
              printf ("    for spanning cell into col %d with remaining span=%d, \
                      new max = %d\n", 
                      colIndex, spanInfo->span, maxColWidth);
          }
        }
        spanInfo->span--;
        if (0==spanInfo->span)
        {
          spanList->RemoveElementAt(spanIndex);
          delete spanInfo;
        }
      }
    }

    // second, process non-fixed-width columns
    if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
    {
      for (rowIndex = 0; rowIndex<numRows; rowIndex++)
      { // this col has proportional width, so determine its width requirements
        nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
        if (nsnull==cellFrame)
        { // there is no cell in this row that corresponds to this column
          continue;
        }
        if (-1==firstRowIndex)
          firstRowIndex = rowIndex;
        if (rowIndex!=cellFrame->GetRowIndex()) {
          // For cells that span rows, we only figure it in once
          NS_ASSERTION(1 != cellFrame->GetRowSpan(), "row index does not match row span");  // sanity check
          continue;
        }
        if (colIndex!=cellFrame->GetColIndex()) {
          // For cells that span cols, we figure in the row using previously-built SpanInfo 
          NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match row span");  // sanity check
          continue;
        }
 
        PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
        nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
        nsSize cellDesiredSize = cellFrame->GetPass1DesiredSize();
        nscoord cellMinWidth=colFrame->GetMinColWidth();
        nscoord cellDesiredWidth=colFrame->GetMaxColWidth();  
        // then get the desired size info factoring in the cell style attributes
        if (1==colSpan)
        {
          cellMinWidth = cellMinSize.width;
          cellDesiredWidth = cellDesiredSize.width;  
          nscoord specifiedCellWidth=-1;
          const nsStylePosition* cellPosition;
          cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
          if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit()) 
          {
            if (PR_FALSE==aTableIsAutoWidth) 
            {
              float percent = cellPosition->mWidth.GetPercentValue();
              specifiedCellWidth = (PRInt32)(aTableSpecifiedWidth*percent);
              if (gsDebug) printf("specified percent width %f of %d = %d\n", 
                                  percent, aTableSpecifiedWidth, specifiedCellWidth);
            }
            // otherwise we need to post-process, set to max for now
            // do we want to set specifiedCellWidth off of aAvailWidth? aMaxWidth?    XXX
          }
          if (-1!=specifiedCellWidth)
          {
            if (specifiedCellWidth>cellMinWidth)
            {
              if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n", 
                                   cellDesiredWidth, specifiedCellWidth);
              cellDesiredWidth = specifiedCellWidth;
            }
          }
        }
        else
        { // colSpan>1, get the proportion for this column
          // then get the desired size info factoring in the cell style attributes
          nscoord effectiveMaxWidthOfSpannedCols = colFrame->GetEffectiveMaxColWidth();
          nscoord effectiveMinWidthOfSpannedCols = colFrame->GetEffectiveMinColWidth();
          for (PRInt32 span=1; span<colSpan; span++)
          {
            nsTableColFrame *nextColFrame = mTableFrame->GetColFrame(colIndex+span);
            if (nsnull==nextColFrame)
              break;
            effectiveMaxWidthOfSpannedCols += nextColFrame->GetEffectiveMaxColWidth();
            effectiveMinWidthOfSpannedCols += nextColFrame->GetEffectiveMinColWidth();
          }
          nscoord specifiedCellWidth=-1;
          const nsStylePosition* cellPosition;
          cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
          if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit()) 
          { //XXX what if table is auto width?
            float percent = cellPosition->mWidth.GetPercentValue();
            specifiedCellWidth = (PRInt32)(aTableSpecifiedWidth*percent);
            if (gsDebug) printf("specified percent width %f of %d = %d\n", 
                                percent, aTableSpecifiedWidth, specifiedCellWidth);
          }
          if (-1!=specifiedCellWidth)
          {
            float percentForThisCol = (float)(cellDesiredSize.width * colFrame->GetEffectiveMaxColWidth()) /
                                      (float)effectiveMaxWidthOfSpannedCols;
            nscoord cellWidthForThisCol = (nscoord)(specifiedCellWidth * percentForThisCol);
            if (cellWidthForThisCol>cellMinWidth)
            {
              if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n", 
                                   cellDesiredWidth, cellWidthForThisCol);
              cellDesiredWidth = cellWidthForThisCol;
            }
          }
          // otherwise it's already been factored in.
          // now, if this column holds the cell, create a spanInfo struct for the cell
          // so subsequent columns can take a proportion of this cell's space into account
          if (cellFrame->GetColIndex()==colIndex)
          { // add this cell to span list iff we are currently processing the column the cell starts in
            SpanInfo *spanInfo = new SpanInfo(colIndex, colSpan-1, cellMinSize.width, cellDesiredSize.width);
            spanInfo->effectiveMaxWidthOfSpannedCols = effectiveMaxWidthOfSpannedCols;
            spanInfo->effectiveMinWidthOfSpannedCols = effectiveMinWidthOfSpannedCols;
            if (nsnull==spanList)
              spanList = new nsVoidArray();
            spanList->AppendElement(spanInfo);
          }
        }

        if (PR_TRUE==gsDebug)
          printf("factoring in cell %d with colSpan=%d\n  factoring in min=%d and desired=%d\n", 
                 rowIndex, colSpan, cellMinWidth, cellDesiredWidth);
        if (maxColWidth < cellDesiredWidth)
          maxColWidth = cellDesiredWidth;
        if (gsDebug==PR_TRUE) 
          printf ("    after cell %d, minColWidth=%d  maxColWidth=%d  effColWidth[%d]=%d\n", 
                  rowIndex, minColWidth, maxColWidth, 
                  colIndex, colFrame->GetEffectiveMaxColWidth());
      } // end looping through cells in the column

      if (gsDebug==PR_TRUE) 
      {
        printf ("  for determining width of col %d %s:\n",
                colIndex, !IsFixedWidth(colPosition, colTableStyle)? "(P)":"(A)");
        printf ("    minColWidth = %d and maxColWidth = %d\n", minColWidth, maxColWidth);
        printf ("    aAvailWidth = %d\n", aAvailWidth);
      }

      // Get column width if it has one
      nscoord specifiedProportionColumnWidth = -1;
      float specifiedPercentageColWidth = -1.0f;
      nscoord specifiedFixedColumnWidth = -1;
      PRBool isAutoWidth = PR_FALSE;
      switch (colPosition->mWidth.GetUnit()) {
        case eStyleUnit_Percent:
          specifiedPercentageColWidth = colPosition->mWidth.GetPercentValue();
          if (gsDebug) printf("column %d has specified percent width = %f\n", colIndex, specifiedPercentageColWidth);
          break;
        case eStyleUnit_Proportional:
          specifiedProportionColumnWidth = colPosition->mWidth.GetIntValue();
          if (gsDebug) printf("column %d has specified percent width = %d\n", colIndex, specifiedProportionColumnWidth);
          break;
        case eStyleUnit_Auto:
          isAutoWidth = PR_TRUE;
          break;
        default:
          break;
      }

      /* set the column width, knowing that the table fits in the available space */
      if (0==specifiedProportionColumnWidth || 0.0==specifiedPercentageColWidth)
      { // col width is specified to be the minimum
        mTableFrame->SetColumnWidth(colIndex, minColWidth);
        if (gsDebug==PR_TRUE) 
          printf ("  3 min: col %d set to min width = %d because style set proportionalWidth=0\n", 
                  colIndex, mTableFrame->GetColumnWidth(colIndex));
      }
      else if ((PR_TRUE==isAutoWidth) || 
               ((PR_TRUE==aTableIsAutoWidth) && (-1==specifiedProportionColumnWidth)))
      {  // col width is determined by the cells' content,
         // so give each remaining column it's desired width (because we know we fit.)
         // if there is width left over, we'll factor that in after this loop is complete
         // the OR clause is because we fix up percentage widths as a post-process 
         // in auto-width tables
        mTableFrame->SetColumnWidth(colIndex, maxColWidth);
        if (gsDebug==PR_TRUE) 
          printf ("  3 auto: col %d with availWidth %d, set to width = %d\n", 
                  colIndex, aAvailWidth, mTableFrame->GetColumnWidth(colIndex));
      }
      else if (-1!=specifiedProportionColumnWidth)
      { // we need to save these and do them after all other columns have been calculated
        /* the calculation will be: 
              sum up n, the total number of slices for the columns with proportional width
              compute the table "required" width, fixed-width + percentage-width +
                the sum of the proportional column's max widths (especially because in this routine I know the table fits)
              compute the remaining width: the required width - the used width (fixed + percentage)
              compute the width per slice
              set the width of each proportional-width column to it's number of slices * width per slice
        */
        mTableFrame->SetColumnWidth(colIndex, 0);   // set the column width to 0, since it isn't computed yet
        if (nsnull==proportionalColumnsList)
          proportionalColumnsList = new nsVoidArray();
        ProportionalColumnLayoutStruct * info = 
          new ProportionalColumnLayoutStruct(colIndex, minColWidth, 
                                             maxColWidth, specifiedProportionColumnWidth);
        proportionalColumnsList->AppendElement(info);
        totalSlices += specifiedProportionColumnWidth;  // keep track of the total number of proportions
        if (gsDebug==PR_TRUE) 
          printf ("  3 proportional: col %d with availWidth %d, gets %d slices with %d slices so far.\n", 
                  colIndex, aAvailWidth, specifiedProportionColumnWidth, totalSlices);
      }
      else
      {  // give the column a percentage of the remaining space
        PRInt32 percentage = -1;
        if (NS_UNCONSTRAINEDSIZE==aAvailWidth)
        { // since the "remaining space" is infinite, give the column it's max requested size
          mTableFrame->SetColumnWidth(colIndex, maxColWidth);
        }
        else
        {
          if (-1.0f != specifiedPercentageColWidth)
          {
            percentage = (PRInt32)(specifiedPercentageColWidth*100.0f); // TODO: rounding errors?
            // base the % on the total specified fixed width of the table
            mTableFrame->SetColumnWidth(colIndex, (percentage*aTableSpecifiedWidth)/100);
            if (gsDebug==PR_TRUE) 
              printf ("  3 percent specified: col %d given %d percent of aTableSpecifiedWidth %d, set to width = %d\n", 
                      colIndex, percentage, aTableSpecifiedWidth, mTableFrame->GetColumnWidth(colIndex));
          }
          if (-1==percentage)
          {
            percentage = 100/mNumCols;
            // base the % on the remaining available width 
            mTableFrame->SetColumnWidth(colIndex, (percentage*aAvailWidth)/100);
            if (gsDebug==PR_TRUE) 
              printf ("  3 percent default: col %d given %d percent of aAvailWidth %d, set to width = %d\n", 
                      colIndex, percentage, aAvailWidth, mTableFrame->GetColumnWidth(colIndex));
          }
          // if the column was computed to be too small, enlarge the column
          if (mTableFrame->GetColumnWidth(colIndex) <= minColWidth)
          {
            mTableFrame->SetColumnWidth(colIndex, minColWidth);
            if (PR_TRUE==gsDebug) printf("  enlarging column to it's minimum = %d\n", minColWidth);
          }
        }
      }
    }
    else
    { // need to maintain this so we know how much we have left over at the end
      nscoord maxEffectiveColWidth = colFrame->GetEffectiveMaxColWidth();
      mTableFrame->SetColumnWidth(colIndex, maxEffectiveColWidth);
      widthOfFixedTableColumns += maxEffectiveColWidth + colInset;
    }
    tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
  }
  tableWidth += colInset;

  /* --- post-process if necessary --- */

  // XXX the following implementation does not account for borders, cell spacing, cell padding

  // first, assign column widths in auto-width tables
  PRInt32 numPercentColumns=0;
  PRInt32 *percentColumns=nsnull;
  mTableFrame->GetColumnsByType(eStyleUnit_Percent, numPercentColumns, percentColumns);
  if ((PR_TRUE==aTableIsAutoWidth) && (0!=numPercentColumns))
  {
    // variables common to lots of code in this block
    nscoord colWidth;
    float percent;
    const nsStylePosition* colPosition;
    nsTableColFrame *colFrame;
    if (numPercentColumns!=mNumCols)
    {
      if (PR_TRUE==gsDebug)
        printf("assigning widths to percent colums in auto-width table\n");
      nscoord widthOfPercentCells=0;  // the total width of those percent-width cells that have been given a width
      nscoord widthOfOtherCells=0;    // the total width of those non-percent-width cells that have been given a width
      float sumOfPercentColumns=0.0f; // the total of the percent widths
      for (colIndex = 0; colIndex<mNumCols; colIndex++)
      {
        // every column contributes to either widthOfOtherCells or widthOfPercentCells, but not both
        if (PR_TRUE==IsColumnInList(colIndex, percentColumns, numPercentColumns))
        {
          colFrame = mTableFrame->GetColFrame(colIndex); 
          colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
          percent = colPosition->mWidth.GetPercentValue(); // we know this will work
          sumOfPercentColumns += percent;
          if (sumOfPercentColumns>1.0f)
            sumOfPercentColumns=1.0f;  // values greater than 100% are meaningless
          widthOfPercentCells += mTableFrame->GetColumnWidth(colIndex);
        }
        else
        {
          widthOfOtherCells += mTableFrame->GetColumnWidth(colIndex);
        }
      }
      if (0==widthOfOtherCells)
        widthOfOtherCells=aMaxWidth;
      if (PR_TRUE==gsDebug)
        printf("  widthOfOtherCells=%d widthOfPercentCells = %d sumOfPercentColumns=%f\n", 
               widthOfOtherCells, widthOfPercentCells, sumOfPercentColumns);
      float remainingPercent = 1.0f - sumOfPercentColumns;
      nscoord newTableWidth;  // the table width after all cells have been resized  
      if (1.0f == sumOfPercentColumns)  // this definately seems like a QUIRK!
        newTableWidth = aMaxWidth;
      else 
      { // the newTableWidth is the larger of the calculation from the percent cells and non-percent cells
        nscoord percentWidth = (nscoord)((1.0f/sumOfPercentColumns)*((float)(widthOfPercentCells)));
        nscoord otherWidth = (nscoord)((1.0f/remainingPercent)*((float)(widthOfOtherCells)));
        if (PR_TRUE==gsDebug)
          printf("    percentWidth=%d otherWidth=%d\n", percentWidth, otherWidth);
        newTableWidth = PR_MAX(percentWidth, otherWidth);     // the table width is the larger of the two computations
        newTableWidth = PR_MIN(newTableWidth, aMaxWidth);     // an auto-width table can't normally be wider than it's parent
        newTableWidth = PR_MIN(newTableWidth, mMaxTableWidth);// an auto-width table can't normally be wider than it's own computed max width 
      }
      nscoord excess = newTableWidth-mFixedTableWidth;      // the amount of new space that needs to be 
                                                            // accounted for in the non-fixed columns
      if (PR_TRUE==gsDebug)
        printf("  newTableWidth=%d \n", newTableWidth);
      PRInt32 i;  // just a counter
      for (i=0; i<numPercentColumns; i++)
      {
        colIndex = percentColumns[i];
        colFrame = mTableFrame->GetColFrame(colIndex);
        colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
        percent = colPosition->mWidth.GetPercentValue(); // we know this will work
        colWidth = (nscoord)((percent)*((float)newTableWidth));
        nscoord minColWidth = colFrame->GetEffectiveMinColWidth();
        colWidth = PR_MAX(colWidth, minColWidth);
        mTableFrame->SetColumnWidth(colIndex, colWidth);
        if (PR_TRUE==gsDebug)
          printf("  col %d with percentwidth=%f set to %d\n", 
                  colIndex, percent, colWidth);
        excess -= colWidth;
      }
      if (0<excess)
      {
        PRInt32 numAutoColumns=0;
        PRInt32 *autoColumns=nsnull;
        mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
        if (PR_TRUE==gsDebug)
          printf("  excess=%d with %d autoColumns\n", excess, numAutoColumns);
        if (0<numAutoColumns)
        {
          nscoord excessPerColumn = excess/numAutoColumns;
          for (i=0; i<numAutoColumns; i++)
          {
            colIndex = autoColumns[i];
            nscoord newWidth = PR_MAX(excessPerColumn, mTableFrame->GetColumnWidth(colIndex));
            if (PR_TRUE==gsDebug)
              printf("    col %d was %d set to \n", 
                     colIndex, mTableFrame->GetColumnWidth(colIndex), newWidth);
            mTableFrame->SetColumnWidth(colIndex, newWidth);
            excess -= excessPerColumn;
          }
          // handle division underflow
          if (0<excess)
          {
            if (PR_TRUE==gsDebug)
              printf("    after first pass through auto-width columns, excess=%d\n", excess);
            for (i=0; i<numAutoColumns; i++)
            {
              colIndex = autoColumns[i];
              nscoord newWidth = 1 + mTableFrame->GetColumnWidth(colIndex);
              mTableFrame->SetColumnWidth(colIndex, newWidth);
            }
          }
        }
      }
    }
    else
    { // all columns have a percent width.  Each already has its desired width.
      // find the smallest percentage and base the widths of the others off that
      PRInt32 indexOfSmallest;
      nscoord colWidthOfSmallest;
      float smallestPercent=2.0f; // an illegal large number
      for (colIndex = 0; colIndex<mNumCols; colIndex++)
      {
        colFrame = mTableFrame->GetColFrame(colIndex); 
        colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
        percent = colPosition->mWidth.GetPercentValue(); // we know this will work 
        if (percent < smallestPercent)
        {
          smallestPercent=percent;
          indexOfSmallest=colIndex;
          colWidthOfSmallest = mTableFrame->GetColumnWidth(colIndex);
        }
        else if (percent==smallestPercent)
        { // the largest desired size among equal-percent columns wins
          nscoord colWidth = mTableFrame->GetColumnWidth(colIndex);
          if (colWidth>colWidthOfSmallest)
          {
            indexOfSmallest = colIndex;
            colWidthOfSmallest = colWidth;
          }
        }
      }
      // now that we know which column to base the other columns' widths off of, do it!
      for (colIndex = 0; colIndex<mNumCols; colIndex++)
      {
        colFrame = mTableFrame->GetColFrame(colIndex); 
        colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
        percent = colPosition->mWidth.GetPercentValue(); // we know this will work 
        if (percent==smallestPercent)
        { // set col width to the max width of all columns that shared the smallest percent-width
          mTableFrame->SetColumnWidth(colIndex, colWidthOfSmallest);  
        }
        else
        {
          colWidth = (nscoord)((percent/smallestPercent)*((float)colWidthOfSmallest));
          nscoord minColWidth = colFrame->GetEffectiveMinColWidth();
          colWidth = PR_MAX(colWidth, minColWidth);
          mTableFrame->SetColumnWidth(colIndex, colWidth); 
        }
        if (PR_TRUE==gsDebug)
          printf("  col %d with percentwidth=%f set to %d\n", 
                  colIndex, percent, colWidth);
      }
    }
  }

  // second, assign column widths to proportional-width columns
  if (nsnull!=proportionalColumnsList)
  {
    // first, figure out the amount of space per slice
    nscoord maxWidthForTable = (0!=aTableSpecifiedWidth) ? aTableSpecifiedWidth : aMaxWidth;
    if (NS_UNCONSTRAINEDSIZE!=maxWidthForTable)
    {
      nscoord widthRemaining = maxWidthForTable - tableWidth;
      nscoord widthPerSlice = widthRemaining/totalSlices;
      PRInt32 numSpecifiedProportionalColumns = proportionalColumnsList->Count();
      for (PRInt32 i=0; i<numSpecifiedProportionalColumns; i++)
      { // for every proportionally-sized column, set the col width to the computed width
        ProportionalColumnLayoutStruct * info = 
          (ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
        // verify that the computed width is at least the minimum width
        nscoord computedColWidth = info->mProportion*widthPerSlice;
        // compute the requested proportional width
        nsTableColFrame *colFrame;
        mTableFrame->GetColumnFrame(info->mColIndex, colFrame);
        nscoord minColWidth = colFrame->GetMinColWidth();
        computedColWidth = PR_MAX(computedColWidth, minColWidth);
        mTableFrame->SetColumnWidth(info->mColIndex, computedColWidth);
        if (gsDebug==PR_TRUE) 
          printf ("  3 proportional step 2: col %d given %d proportion of remaining space %d, set to width = %d\n", 
                  info->mColIndex, info->mProportion, widthRemaining, computedColWidth);
        tableWidth += computedColWidth;
        delete info;
      }
    }
    else
    { // we're in an unconstrained situation, so give each column the max of the max column widths
      PRInt32 numSpecifiedProportionalColumns = proportionalColumnsList->Count();
      PRInt32 maxOfMaxColWidths = 0;
      PRInt32 i;
      for (i=0; i<numSpecifiedProportionalColumns; i++)
      {
        ProportionalColumnLayoutStruct * info = 
          (ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
        if (maxOfMaxColWidths < info->mMaxColWidth)
          maxOfMaxColWidths = info->mMaxColWidth;
      }
      for (i=0; i<numSpecifiedProportionalColumns; i++)
      {
        ProportionalColumnLayoutStruct * info = 
          (ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
        mTableFrame->SetColumnWidth(info->mColIndex, maxOfMaxColWidths);
        if (gsDebug==PR_TRUE) 
          printf ("  3 proportional step 2 (unconstrained!): col %d set to width = %d\n", 
                  info->mColIndex, maxOfMaxColWidths);
        tableWidth += maxOfMaxColWidths;
        nsTableColFrame *colFrame;
        mTableFrame->GetColumnFrame(info->mColIndex, colFrame);
        colFrame->SetEffectiveMaxColWidth(maxOfMaxColWidths);
        delete info;
      }
    }
    delete proportionalColumnsList;
  }

  // next, account for table width constraints
  // if the caption min width is wider than than the table, expand the table
  nscoord captionMinWidth=mTableFrame->GetMinCaptionWidth();
  if (captionMinWidth>tableWidth)
  {
    DistributeExcessSpace(captionMinWidth, tableWidth, widthOfFixedTableColumns);
  }
  else
  { // else, if the caption min width is not an issue...
    /* if the specified width of the table is greater than the table's computed width, expand the
     * table's computed width to match the specified width, giving the extra space to proportionately-sized
     * columns if possible. */
    if ((PR_FALSE==aTableIsAutoWidth) && (aAvailWidth > (tableWidth-widthOfFixedTableColumns)) &&
        (gBigSpace!=aAvailWidth))
    {
      DistributeExcessSpace(aAvailWidth, tableWidth, widthOfFixedTableColumns);
    }
    // IFF the table is NOT (auto-width && all columns have fixed width)
    PRInt32 numFixedColumns=0;
    PRInt32 *fixedColumns=nsnull;
    mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedColumns, fixedColumns);
    if (!((PR_TRUE==aTableIsAutoWidth) && (numFixedColumns==mNumCols)))
    {
      nscoord computedWidth=0;
      for (PRInt32 i=0; i<mNumCols; i++) {
        computedWidth += mTableFrame->GetColumnWidth(i) + colInset;
      }
      if (computedWidth>aMaxWidth) {
        AdjustTableThatIsTooWide(computedWidth, aMaxWidth, PR_FALSE);
      }
    }
  }

  // cleanup
  if (nsnull!=spanList)
  {
    if (gsDebug) printf("BTLS::BCTFits...space leak, span list not empty");
    delete spanList;
  }
  return result;
}

// take the extra space in the table and distribute it proportionately (based on desired width)
// give extra space to auto-width cells first, or if there are none to all cells
void BasicTableLayoutStrategy::DistributeExcessSpace(nscoord  aAvailWidth,
                                                     nscoord  aTableWidth, 
                                                     nscoord  aWidthOfFixedTableColumns)
{
  nscoord excess = 0;
  if (PR_TRUE==gsDebug) printf ("DistributeExcessSpace: fixed width %d > computed table width %d, woftc=%d\n",
                                aAvailWidth, aTableWidth, aWidthOfFixedTableColumns);
  nscoord  widthMinusFixedColumns = aTableWidth - aWidthOfFixedTableColumns;
  // if there are auto-sized columns, give them the extra space
  // the trick here is to do the math excluding non-auto width columns
  PRInt32 numAutoColumns=0;
  PRInt32 *autoColumns=nsnull;
  GetColumnsThatActLikeAutoWidth(numAutoColumns, autoColumns);  // allocates autoColumns, be sure to delete it
  if (0!=numAutoColumns)
  {
    // there's at least one auto-width column, so give it (them) the extra space
    // proportionately distributed extra space, based on the column's desired size
    nscoord totalEffectiveWidthOfAutoColumns = 0;
    // 1. first, get the total width of the auto columns
    PRInt32 i;
    for (i = 0; i<numAutoColumns; i++)
    {
      nsTableColFrame *colFrame;
      mTableFrame->GetColumnFrame(autoColumns[i], colFrame);
      nscoord effectiveColWidth = colFrame->GetEffectiveMaxColWidth();
      if (0!=effectiveColWidth)
        totalEffectiveWidthOfAutoColumns += effectiveColWidth;
      else
        totalEffectiveWidthOfAutoColumns += mTableFrame->GetColumnWidth(autoColumns[i]);
    }
    // excess is the amount of space that was available minus the computed available width
    // XXX shouldn't it just be aMaxWidth - aTableWidth???
    if (gsDebug==PR_TRUE) 
      printf("  aAvailWidth specified as %d, expanding columns by excess = %d\n", 
             aAvailWidth, excess);
    excess = aAvailWidth - widthMinusFixedColumns;

    // 2. next, compute the proportion to be added to each column, and add it
    nscoord totalAdded=0;
    for (i = 0; i<numAutoColumns; i++)
    {
      PRInt32 colIndex = autoColumns[i];
      nsTableColFrame *colFrame;
      mTableFrame->GetColumnFrame(colIndex, colFrame);
      nscoord oldColWidth = mTableFrame->GetColumnWidth(colIndex);
      float percent;
      if (0!=totalEffectiveWidthOfAutoColumns)
        percent = ((float)(colFrame->GetEffectiveMaxColWidth()))/((float)totalEffectiveWidthOfAutoColumns);
      else
        percent = ((float)1)/((float)numAutoColumns);
      nscoord excessForThisColumn = (nscoord)(excess*percent);
      totalAdded += excessForThisColumn;
      nscoord colWidth = excessForThisColumn+oldColWidth;
      if (gsDebug==PR_TRUE) 
        printf("  distribute excess to auto columns:  column %d was %d, now set to %d\n", 
               colIndex, oldColWidth, colWidth);
      mTableFrame->SetColumnWidth(colIndex, colWidth);
    }
    if (PR_TRUE==gsDebug) 
      printf("lost a few twips due to rounding errors: excess=%d, totalAdded=%d\n", excess, totalAdded);
  }
  // otherwise, distribute the space between all the columns 
  // (they must be all fixed and percentage-width columns, or we would have gone into the block above)
  else
  {
    excess = aAvailWidth - widthMinusFixedColumns;
    if (gsDebug==PR_TRUE) 
      printf("  aAvailWidth specified as %d, expanding columns by excess = %d\n", 
             aAvailWidth, excess);
    nscoord totalAdded=0;
    for (PRInt32 colIndex = 0; colIndex<mNumCols; colIndex++)
    {
      nscoord oldColWidth=0;
      if (0==oldColWidth)
        oldColWidth = mTableFrame->GetColumnWidth(colIndex);
      float percent;
      if (0!=aTableWidth)
        percent = (float)oldColWidth/(float)aTableWidth;
      else
        percent = (float)1/(float)mNumCols;
      nscoord excessForThisColumn = (nscoord)(NSToCoordRound(excess*percent));
      totalAdded += excessForThisColumn;
      nscoord colWidth = excessForThisColumn+oldColWidth;
      if (gsDebug==PR_TRUE) 
        printf("  distribute excess: column %d was %d, now %d from %=%f\n", 
               colIndex, oldColWidth, colWidth, percent);
      mTableFrame->SetColumnWidth(colIndex, colWidth);
    }
    if (PR_TRUE==gsDebug) 
      printf("lost a few twips due to rounding errors: excess=%d, totalAdded=%d\n", excess, totalAdded);
  }
  if (nsnull!=autoColumns)
    delete [] autoColumns;
}

/* assign columns widths for a table whose max size doesn't fit in the available space
 */
PRBool BasicTableLayoutStrategy::BalanceColumnsConstrained( const nsHTMLReflowState& aReflowState,
                                                            nscoord aAvailWidth,
                                                            nscoord aMaxWidth,
                                                            PRBool  aTableIsAutoWidth)
{
#ifdef NS_DEBUG
  nsIFrame *tablePIF=nsnull;
  mTableFrame->GetPrevInFlow(tablePIF);
  NS_ASSERTION(nsnull==tablePIF, "never ever call me on a continuing frame!");
#endif

  PRBool result = PR_TRUE;
  PRInt32 maxOfAllMinColWidths = 0;   // for the case where we have equal column widths, this is the smallest a column can be
  nscoord tableWidth=0;               // the width of the table as a result of setting column widths
  PRInt32 totalSlices=0;              // the total number of slices the proportional-width columns request
  nsVoidArray *proportionalColumnsList=nsnull; // a list of the columns that are proportional-width
  PRBool equalWidthColumns = PR_TRUE; // remember if we're in the special case where all
                                      // proportional-width columns are equal (if any are anything other than 1)
  PRBool atLeastOneAutoWidthColumn = PR_FALSE;  // true if at least one column is auto-width, requiring us to post-process
  nsVoidArray *spanList=nsnull;       // a list of the cells that span columns
  PRInt32 numRows = mTableFrame->GetRowCount();
  nscoord colInset = mTableFrame->GetCellSpacingX();

  for (PRInt32 colIndex = 0; colIndex<mNumCols; colIndex++)
  { 
    if (gsDebug==PR_TRUE) printf ("  for col %d\n", colIndex);
    nscoord minColWidth = 0;
    nscoord maxColWidth = 0;
    
    // Get column information
    nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
    NS_ASSERTION(nsnull!=colFrame, "bad col frame");
    // Get the columns's style and margins
    PRInt32 rowIndex;
    PRInt32 firstRowIndex = -1;
    const nsStylePosition* colPosition;
    colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
    const nsStyleTable* colTableStyle;
    colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);

    // first, deal with any cells that span into this column from a pervious column
      // go through the list backwards so we can delete easily
    if (nsnull!=spanList)
    {
      PRInt32 spanCount = spanList->Count();
      // go through the list backwards so we can delete easily
      for (PRInt32 spanIndex=spanCount-1; 0<=spanIndex; spanIndex--)
      {
        SpanInfo *spanInfo = (SpanInfo *)(spanList->ElementAt(spanIndex));
        if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
        {
          // compute the spanning cell's contribution to the column min width
          nscoord spanCellMinWidth;
          PRBool needsExtraMinWidth = PR_FALSE;
          /*
          if (spanInfo->effectiveMinWidthOfSpannedCols<spanInfo->cellMinWidth)
            needsExtraMinWidth = PR_TRUE;
            */
          if (PR_TRUE==needsExtraMinWidth)
          {
            if (0!=spanInfo->effectiveMinWidthOfSpannedCols)
            {
              spanCellMinWidth = (spanInfo->cellMinWidth * colFrame->GetEffectiveMinColWidth()) /
                                 (spanInfo->effectiveMinWidthOfSpannedCols);
              if (gsDebug==PR_TRUE) 
                printf ("    spanlist min: %d of %d\n", spanCellMinWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
              if (minColWidth < spanCellMinWidth)
              {
                minColWidth = spanCellMinWidth;     // set the new min width for the col
                if (gsDebug==PR_TRUE) 
                  printf ("    for spanning cell into col %d with remaining span=%d, new min = %d\n", 
                          colIndex, spanInfo->span, minColWidth);
                maxColWidth = PR_MAX(maxColWidth, minColWidth);
                if (gsDebug) 
                  printf ("    maxColWidth = %d\n", maxColWidth);
              }
            }
            else
            {
              spanCellMinWidth = spanInfo->cellMinWidth/spanInfo->initialColSpan;
              if (minColWidth < spanCellMinWidth)
              {
                minColWidth = spanCellMinWidth;
                if (gsDebug==PR_TRUE) 
                  printf ("    for spanning cell into col %d with remaining span=%d, new min = %d\n", 
                          colIndex, spanInfo->span, minColWidth);
                maxColWidth = PR_MAX(maxColWidth, minColWidth);
                if (gsDebug) 
                  printf ("    maxColWidth = %d\n", maxColWidth);  
              }
            }
          }


          // compute the spanning cell's contribution to the column max width
          nscoord spanCellMaxWidth;
          if (0!=spanInfo->effectiveMaxWidthOfSpannedCols)
          {
            spanCellMaxWidth = (spanInfo->cellDesiredWidth * colFrame->GetEffectiveMinColWidth()) /
                               (spanInfo->effectiveMaxWidthOfSpannedCols);
            if (gsDebug==PR_TRUE) 
              printf ("    spanlist max: %d of %d\n", spanCellMaxWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
            if (maxColWidth < spanCellMaxWidth)
            {
              maxColWidth = spanCellMaxWidth;                 // set the new max width for the col
              mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);  // set the column to the new desired max width
              if (gsDebug==PR_TRUE) 
                printf ("    for spanning cell into col %d with remaining span=%d, \
                        new max = %d\n", 
                        colIndex, spanInfo->span, maxColWidth);
            }
          }
          else
          {
            spanCellMaxWidth = spanInfo->cellDesiredWidth/spanInfo->initialColSpan;
            maxColWidth = spanCellMaxWidth;
            mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);
            if (gsDebug==PR_TRUE) 
              printf ("    for spanning cell into col %d with remaining span=%d, \
                      new max = %d\n", 
                      colIndex, spanInfo->span, maxColWidth);
          }
        }
        spanInfo->span--;
        if (0==spanInfo->span)
        {
          spanList->RemoveElementAt(spanIndex);
          delete spanInfo;
        }
      }
    }

    // second, process non-fixed-width columns
    if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
    {
      for (rowIndex = 0; rowIndex<numRows; rowIndex++)
      {
        nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
        if (nsnull==cellFrame)
        { // there is no cell in this row that corresponds to this column
          continue;
        }
        if (rowIndex!=cellFrame->GetRowIndex()) {
          // For cells that span rows, we only figure it in once
          NS_ASSERTION(1 != cellFrame->GetRowSpan(), "row index does not match row span");  // sanity check
          continue;
        }
        if (colIndex!=cellFrame->GetColIndex()) {
          // For cells that span cols, we figure in the row using previously-built SpanInfo 
          NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match row span");  // sanity check
          continue;
        }

        PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
        nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
        nsSize cellDesiredSize = cellFrame->GetPass1DesiredSize();

        nscoord cellMinWidth=colFrame->GetAdjustedMinColWidth();
        nscoord cellDesiredWidth=colFrame->GetMaxColWidth(); 
        if (1==colSpan)
        {
          cellMinWidth = cellMinSize.width;
          cellDesiredWidth = cellDesiredSize.width;  
          nscoord specifiedCellWidth=-1;
          const nsStylePosition* cellPosition;
          cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
          if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit()) 
          {
            float percent = cellPosition->mWidth.GetPercentValue();
            specifiedCellWidth = (PRInt32)(aMaxWidth*percent);
            if (gsDebug) printf("specified percent width %f of %d = %d\n", 
                                percent, aMaxWidth, specifiedCellWidth);
          }
          if (-1!=specifiedCellWidth)
          {
            if (specifiedCellWidth>cellMinWidth)
            {
              if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n", 
                                   cellDesiredWidth, specifiedCellWidth);
              cellDesiredWidth = specifiedCellWidth;
            }
          }
        }
        else
        { // colSpan>1, get the proportion for this column
          // then get the desired size info factoring in the cell style attributes
          nscoord effectiveMaxWidthOfSpannedCols = colFrame->GetEffectiveMaxColWidth();
          nscoord effectiveMinWidthOfSpannedCols = colFrame->GetEffectiveMinColWidth();
          for (PRInt32 span=1; span<colSpan; span++)
          {
            nsTableColFrame *nextColFrame = mTableFrame->GetColFrame(colIndex+span);
            if (nsnull==nextColFrame)
              break;
            effectiveMaxWidthOfSpannedCols += nextColFrame->GetEffectiveMaxColWidth();
            effectiveMinWidthOfSpannedCols += nextColFrame->GetEffectiveMinColWidth();
          }
          nscoord specifiedCellWidth=-1;
          const nsStylePosition* cellPosition;
          cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
          if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit()) 
          { //XXX what if table is auto width?
            float percent = cellPosition->mWidth.GetPercentValue();
            specifiedCellWidth = (PRInt32)(aMaxWidth*percent);
            if (gsDebug) printf("specified percent width %f of %d = %d\n", 
                                percent, aMaxWidth, specifiedCellWidth);
          }
          if (-1!=specifiedCellWidth)
          {
            float percentForThisCol = (float)(cellDesiredSize.width * colFrame->GetEffectiveMaxColWidth()) /
                                      (float)effectiveMaxWidthOfSpannedCols;
            nscoord cellWidthForThisCol = (nscoord)(specifiedCellWidth * percentForThisCol);
            if (cellWidthForThisCol>cellMinWidth)
            {
              if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n", 
                                   cellDesiredWidth, cellWidthForThisCol);
              cellDesiredWidth = cellWidthForThisCol;
            }
          }
          // otherwise it's already been factored in.
          // now, if this column holds the cell, create a spanInfo struct for the cell
          // so subsequent columns can take a proportion of this cell's space into account
          if (cellFrame->GetColIndex()==colIndex)
          { // add this cell to span list iff we are currently processing the column the cell starts in
            SpanInfo *spanInfo = new SpanInfo(colIndex, colSpan-1, cellMinSize.width, cellDesiredSize.width);
            spanInfo->effectiveMaxWidthOfSpannedCols = effectiveMaxWidthOfSpannedCols;
            spanInfo->effectiveMinWidthOfSpannedCols = effectiveMinWidthOfSpannedCols;
            if (nsnull==spanList)
              spanList = new nsVoidArray();
            spanList->AppendElement(spanInfo);
          }
        }

        if (PR_TRUE==gsDebug)
          printf("factoring in cell %d with colSpan=%d\n  factoring in min=%d and desired=%d\n", 
                 rowIndex, colSpan, cellMinWidth, cellDesiredWidth);
        // remember the widest min cell width
        if (minColWidth < cellMinWidth)
          minColWidth = cellMinWidth;
        // remember the max desired cell width
        if (maxColWidth < cellDesiredWidth)
          maxColWidth = cellDesiredWidth;
        // effectiveMaxColumnWidth is the width as if no cells with colspans existed
//        if ((1==colSpan) && (colFrame->GetEffectiveMaxColWidth() < maxColWidth))
//          colFrame->SetEffectiveMaxColWidth(cellDesiredWidth);
        if (gsDebug==PR_TRUE) 
          printf ("    after cell %d, minColWidth=%d  maxColWidth=%d  effColWidth[%d]=%d,%d\n", 
                  rowIndex, minColWidth, maxColWidth, 
                  colIndex, colFrame->GetEffectiveMinColWidth(), colFrame->GetEffectiveMaxColWidth());
      }

      if (gsDebug==PR_TRUE) 
      {
        printf ("  for determining width of col %d %s:\n",
                colIndex, !IsFixedWidth(colPosition, colTableStyle)? "(P)":"(A)");
        printf ("    minTableWidth = %d and maxTableWidth = %d\n", 
                mMinTableWidth, mMaxTableWidth);
        printf ("    minColWidth = %d, maxColWidth = %d, eff=%d,%d\n", 
                minColWidth, maxColWidth, colFrame->GetEffectiveMinColWidth(), 
                colFrame->GetEffectiveMaxColWidth());
        printf ("    aAvailWidth = %d\n", aAvailWidth);
      }

      // Get column width if it has one
      nscoord specifiedProportionColumnWidth = -1;
      float specifiedPercentageColWidth = -1.0f;
      PRBool isAutoWidth = PR_FALSE;
      switch (colPosition->mWidth.GetUnit()) {
        case eStyleUnit_Percent:
          specifiedPercentageColWidth = colPosition->mWidth.GetPercentValue();
          if (gsDebug) printf("column %d has specified percent width = %f\n", colIndex, specifiedPercentageColWidth);
          break;
        case eStyleUnit_Proportional:
          specifiedProportionColumnWidth = colPosition->mWidth.GetIntValue();
          if (gsDebug) printf("column %d has specified percent width = %d\n", colIndex, specifiedProportionColumnWidth);
          break;
        case eStyleUnit_Auto:
          isAutoWidth = PR_TRUE;
          break;
        default:
          break;
      }

      /* set the column width, knowing that the table is constrained */
      if (0==specifiedProportionColumnWidth || 0.0==specifiedPercentageColWidth)
      { // col width is specified to be the minimum
        mTableFrame->SetColumnWidth(colIndex, minColWidth);
        if (gsDebug==PR_TRUE) 
          printf ("  4 (0): col %d set to min width = %d because style set proportionalWidth=0\n", 
                  colIndex, mTableFrame->GetColumnWidth(colIndex));
      }
      else if (1==mNumCols)
      { // there is only one column, and we know that it's desired width doesn't fit
        // so the column should be as wide as the available space allows it to be minus cell spacing
        nscoord colWidth = aAvailWidth - (2*colInset);
        mTableFrame->SetColumnWidth(colIndex, colWidth);
        if (gsDebug==PR_TRUE) 
          printf ("  4 one-col:  col %d set to width = %d from available width %d and cell spacing %d\n", 
                  colIndex, mTableFrame->GetColumnWidth(colIndex), aAvailWidth, colInset);
      }
      else if (PR_TRUE==isAutoWidth)
      { // column's width is determined by its content, done in post-processing
        mTableFrame->SetColumnWidth(colIndex, minColWidth); // reserve the column's min width
        atLeastOneAutoWidthColumn = PR_TRUE;
      }
      else if (-1!=specifiedProportionColumnWidth)
      { // we need to save these and do them after all other columns have been calculated
        /* the calculation will be: 
              sum up n, the total number of slices for the columns with proportional width
              compute the table "required" width, fixed-width + percentage-width +
                the sum of the proportional column's max widths (especially because in this routine I know the table fits)
              compute the remaining width: the required width - the used width (fixed + percentage)
              compute the width per slice
              set the width of each proportional-width column to it's number of slices * width per slice
        */
        mTableFrame->SetColumnWidth(colIndex, 0);   // set the column width to 0, since it isn't computed yet
        if (nsnull==proportionalColumnsList)
          proportionalColumnsList = new nsVoidArray();
        ProportionalColumnLayoutStruct * info = 
          new ProportionalColumnLayoutStruct(colIndex, minColWidth, maxColWidth, specifiedProportionColumnWidth);
        proportionalColumnsList->AppendElement(info);
        totalSlices += specifiedProportionColumnWidth;
        if (1!=specifiedProportionColumnWidth)
          equalWidthColumns = PR_FALSE;
      }
      else
      {  // give the column a percentage of the remaining space
        PRInt32 percentage = -1;
        if (NS_UNCONSTRAINEDSIZE==aAvailWidth)
        { // since the "remaining space" is infinite, give the column it's max requested size
          mTableFrame->SetColumnWidth(colIndex, maxColWidth);
        }
        else
        {
          if (-1.0f != specifiedPercentageColWidth)
          {
            percentage = (PRInt32)(specifiedPercentageColWidth*100.0f); // TODO: rounding errors?
            // base the % on the total specified fixed width of the table
            mTableFrame->SetColumnWidth(colIndex, (percentage*aMaxWidth)/100);
            if (gsDebug==PR_TRUE) 
              printf ("  4 percent specified: col %d given %d percent of aMaxWidth %d, set to width = %d\n", 
                      colIndex, percentage, aMaxWidth, mTableFrame->GetColumnWidth(colIndex));
          }
          if (-1==percentage)
          {
            percentage = 100/mNumCols;
            // base the % on the remaining available width 
            mTableFrame->SetColumnWidth(colIndex, (percentage*aAvailWidth)/100);
            if (gsDebug==PR_TRUE) 
              printf ("  4 percent default: col %d given %d percent of aAvailWidth %d, set to width = %d\n", 
                      colIndex, percentage, aAvailWidth, mTableFrame->GetColumnWidth(colIndex));
          }
          // if the column was computed to be too small, enlarge the column
          if (mTableFrame->GetColumnWidth(colIndex) <= minColWidth)
          {
            mTableFrame->SetColumnWidth(colIndex, minColWidth);
            if (PR_TRUE==gsDebug) printf("  enlarging column to it's minimum = %d\n", minColWidth);
            if (maxOfAllMinColWidths < minColWidth)
            {
              maxOfAllMinColWidths = minColWidth;
              if (PR_TRUE==gsDebug)
                printf("   and setting maxOfAllMins to %d\n", maxOfAllMinColWidths);
            }
          }
        }
      }
    }
    else
    {
      mTableFrame->SetColumnWidth(colIndex, colFrame->GetMaxColWidth());
    }
    tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
  }
  tableWidth += colInset;
  /* --- post-process if necessary --- */
  // first, assign autoWidth columns a width
  if (PR_TRUE==atLeastOneAutoWidthColumn)
  { // proportionately distribute the remaining space to autowidth columns
    // "0" for the last param tells DistributeRemainingSpace that this is the top (non-recursive) call
    PRInt32 topRecursiveControl=0;
    DistributeRemainingSpace(aMaxWidth, tableWidth, aTableIsAutoWidth, topRecursiveControl);
  }
  
  // second, fix up tables where column width attributes give us a table that is too wide or too narrow
  nscoord computedWidth=colInset;
  for (PRInt32 i=0; i<mNumCols; i++) {
    computedWidth += mTableFrame->GetColumnWidth(i) + colInset;
  }
  if (computedWidth<aMaxWidth) 
  { // then widen the table because it's too narrow
    // do not widen auto-width tables, they shrinkwrap to their content's width
    if (PR_FALSE==aTableIsAutoWidth)
      AdjustTableThatIsTooNarrow(computedWidth, aMaxWidth);
  }
  else if (computedWidth>aMaxWidth) 
  { // then shrink the table width because its too wide
    AdjustTableThatIsTooWide(computedWidth, aMaxWidth, PR_FALSE);
  }


  // finally, assign a width to proportional-width columns
  if (nsnull!=proportionalColumnsList)
  {
    // first, figure out the amount of space per slice
    nscoord widthRemaining = aMaxWidth - tableWidth;
    nscoord widthPerSlice = widthRemaining/totalSlices;
    PRInt32 numSpecifiedProportionalColumns = proportionalColumnsList->Count();
    for (PRInt32 i=0; i<numSpecifiedProportionalColumns; i++)
    {
      ProportionalColumnLayoutStruct * info = 
        (ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
      if (PR_TRUE==equalWidthColumns && 0!=maxOfAllMinColWidths)
      {
        if (gsDebug==PR_TRUE) 
          printf("  EqualColWidths specified and some column couldn't fit, so setting col %d width to %d\n", 
                 info->mColIndex, maxOfAllMinColWidths);
        mTableFrame->SetColumnWidth(info->mColIndex, maxOfAllMinColWidths);
      }
      else
      {
        // compute the requested proportional width
        nscoord computedColWidth = info->mProportion*widthPerSlice;
        // verify that the computed width is at least the minimum width
        nsTableColFrame *colFrame;
        mTableFrame->GetColumnFrame(info->mColIndex, colFrame);
        nscoord minColWidth = colFrame->GetMinColWidth();
        computedColWidth = PR_MAX(computedColWidth, minColWidth);
        mTableFrame->SetColumnWidth(info->mColIndex, computedColWidth);
        if (gsDebug==PR_TRUE) 
          printf ("  4 proportion: col %d given %d proportion of remaining space %d, set to width = %d\n", 
                  info->mColIndex, info->mProportion, widthRemaining, computedColWidth);
      }
      delete info;
    }
    delete proportionalColumnsList;
  }

  // clean up
  if (nsnull!=spanList)
  {
    if (gsDebug) printf("BTLS::BCTConstrained...space leak, span list not empty");
    delete spanList;
  }

  return result;
}

static const PRInt32 kRecursionLimit=10;  // backwards compatible with Nav4

// take the remaining space in the table and distribute it proportionately 
// to the auto-width cells in the table (based on desired width)
void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord  aTableSpecifiedWidth,
                                                        nscoord &aComputedTableWidth, 
                                                        PRBool   aTableIsAutoWidth,
                                                        PRInt32 &aRecursionControl)
{
  aRecursionControl++;
  if (kRecursionLimit<=aRecursionControl) // only allow kRecursionLimit iterations, as per Nav4. See laytable.c
    return;
  nscoord sumOfMinWidths = 0;   // sum of min widths of each auto column
  nscoord startingComputedTableWidth = aComputedTableWidth;  // remember this so we can see if we're making any progress
  if (PR_TRUE==gsDebug) 
    printf ("DistributeRemainingSpace: fixed width %d > computed table width %d\n",
            aTableSpecifiedWidth, aComputedTableWidth);
  // if there are auto-sized columns, give them the extra space
  PRInt32 numAutoColumns=0;
  PRInt32 *autoColumns=nsnull;
  mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
  if (0!=numAutoColumns)
  {
    PRInt32 numColumnsToBeResized=0;
    // there's at least one auto-width column, so give it (them) the extra space
    // proportionately distributed extra space, based on the column's desired size
    nscoord totalEffectiveWidthOfAutoColumns = 0;
    // 1. first, get the total width of the auto columns
    PRInt32 i;
    for (i = 0; i<numAutoColumns; i++)
    {
      PRInt32 colIndex = autoColumns[i];
      nsTableColFrame *colFrame = mTableFrame->GetColFrame(autoColumns[i]);
      nscoord startingColWidth = mTableFrame->GetColumnWidth(colIndex);
      nscoord maxEffectiveColWidth = colFrame->GetEffectiveMaxColWidth();
      if ((PR_FALSE==aTableIsAutoWidth)||(startingColWidth<maxEffectiveColWidth))
      {
        numColumnsToBeResized++;
        if (0!=maxEffectiveColWidth)
          totalEffectiveWidthOfAutoColumns += maxEffectiveColWidth;
        else
          totalEffectiveWidthOfAutoColumns += mTableFrame->GetColumnWidth(autoColumns[i]);
      }
    }
    // availWidth is the difference between the total available width and the 
    // amount of space already assigned, assuming auto col widths were assigned 0.
    nscoord availWidth;
    availWidth = aTableSpecifiedWidth - aComputedTableWidth;
    if (gsDebug==PR_TRUE) 
      printf("  aTableSpecifiedWidth specified as %d, availWidth is = %d\n", 
             aTableSpecifiedWidth, availWidth);
    
    // 2. next, compute the proportion to be added to each column, and add it
    for (i = 0; i<numAutoColumns; i++)
    {
      PRInt32 colIndex = autoColumns[i];
      nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
      nscoord startingColWidth = mTableFrame->GetColumnWidth(colIndex);
      nscoord maxEffectiveColWidth = colFrame->GetEffectiveMaxColWidth();
      // if we actually have room to distribute, do it here
      // otherwise, the auto columns already are set to their minimum
      if (0<availWidth)
      {
        if ((PR_FALSE==aTableIsAutoWidth)||(startingColWidth<maxEffectiveColWidth))
        {
          float percent;
          if (0!=aTableSpecifiedWidth)
            percent = ((float)maxEffectiveColWidth)/((float)totalEffectiveWidthOfAutoColumns);
          else
            percent = ((float)1)/((float)numColumnsToBeResized);
          nscoord colWidth =  startingColWidth + NSToCoordRound(((float)(availWidth))*percent);
          if (PR_TRUE==gsDebug)
            printf("  colWidth = %d from startingColWidth %d plus %d percent of availWidth %d\n",
                  colWidth, startingColWidth, (nscoord)((float)100*percent), availWidth);
          // in an auto width table, the column cannot be wider than its max width
          if (PR_TRUE==aTableIsAutoWidth)
          { // since the table shrinks to the content width, don't be wider than the content max width
            colWidth = PR_MIN(colWidth, colFrame->GetMaxColWidth());
          }
          aComputedTableWidth += colWidth - startingColWidth;
          if (gsDebug==PR_TRUE) 
            printf("  distribute width to auto columns:  column %d was %d, now set to %d\n", 
                   colIndex, colFrame->GetEffectiveMaxColWidth(), colWidth);
          mTableFrame->SetColumnWidth(colIndex, colWidth);
        }
      }
    }

    if (aComputedTableWidth!=startingComputedTableWidth)
    {  // othewise we made no progress and shouldn't continue
      if (aComputedTableWidth<aTableSpecifiedWidth)
      {
        DistributeRemainingSpace(aTableSpecifiedWidth, aComputedTableWidth, aTableIsAutoWidth, aRecursionControl);
      }
    }
  }
  if (PR_TRUE==gsDebug)
  {
    nscoord tableWidth=0;
    printf("at end of DistributeRemainingSpace: ");
    for (PRInt32 i=0; i<mNumCols; i++) 
    {
      tableWidth += mTableFrame->GetColumnWidth(i);
      printf(" %d ", mTableFrame->GetColumnWidth(i));
    }
    printf ("\n  computed table width is %d\n",tableWidth);
  }
}


void BasicTableLayoutStrategy::AdjustTableThatIsTooWide(nscoord aComputedWidth, 
                                                        nscoord aTableWidth, 
                                                        PRBool  aShrinkFixedCols)
{
  if (PR_TRUE==gsDebug)
  {
    nscoord tableWidth=0;
    printf("before AdjustTableThatIsTooWide: ");
    for (PRInt32 i=0; i<mNumCols; i++) 
    {
      tableWidth += mTableFrame->GetColumnWidth(i);
      printf(" %d ", mTableFrame->GetColumnWidth(i));
    }
    printf ("\n  computed table width is %d\n",tableWidth);
  }

  PRInt32 numFixedColumns=0;
  PRInt32 *fixedColumns=nsnull;
  mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedColumns, fixedColumns);
  PRInt32 numAutoColumns=0;
  PRInt32 *autoColumns=nsnull;
  mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
  nscoord excess = aComputedWidth - aTableWidth;
  nscoord minDiff;    // the smallest non-zero delta between a column's current width and its min width
  PRInt32 * colsToShrink = new PRInt32[mNumCols];
  // while there is still extra computed space in the table
  while (0<excess)
  {
    // reinit state variables
    PRInt32 colIndex;
    for (colIndex=0; colIndex<mNumCols; colIndex++)
      colsToShrink[colIndex]=0;
    minDiff=0;

    // determine what columns we can remove width from
    PRInt32 numColsToShrink = 0;
    PRBool shrinkAutoOnly=PR_TRUE;
    PRBool keepLooking=PR_TRUE;
    while (PR_TRUE==keepLooking)
    {
      for (colIndex=0; colIndex<mNumCols; colIndex++)
      {
        nscoord currentColWidth = mTableFrame->GetColumnWidth(colIndex);
        nsTableColFrame *colFrame;
        mTableFrame->GetColumnFrame(colIndex, colFrame);
        nscoord minColWidth = colFrame->GetAdjustedMinColWidth();
        if (currentColWidth==minColWidth)
          continue;
        if ((PR_FALSE==aShrinkFixedCols) && 
            (PR_TRUE==IsColumnInList(colIndex, fixedColumns, numFixedColumns)))
          continue;
        if ((PR_TRUE==shrinkAutoOnly) && 
            (PR_FALSE==IsColumnInList(colIndex, autoColumns, numAutoColumns)))
          continue;
        colsToShrink[numColsToShrink] = colIndex;
        numColsToShrink++;
        nscoord diff = currentColWidth - minColWidth;
        if ((0==minDiff) || (diff<minDiff))
          minDiff = diff;
      }
      if (PR_FALSE==shrinkAutoOnly)
        keepLooking=PR_FALSE; // we've looked everywhere, so bail.  this breaks us out of the loop
      if (0!=numColsToShrink)
        keepLooking=PR_FALSE; // we found at least one column to shrink, so bail.  this breaks us out of the loop
      shrinkAutoOnly=PR_FALSE;// this guarantees we'll go through this loop only one more time
    }
    // if there are no columns we can remove space from, we're done
    if (0==numColsToShrink)
      break;
    
    // determine the amount to remove from each column
    nscoord excessPerColumn;
    if (excess<numColsToShrink)
      excessPerColumn=1;
    else
      excessPerColumn = excess/numColsToShrink;  // guess we can remove as much as we want
    if (excessPerColumn>minDiff)                 // then adjust for minimum col widths
      excessPerColumn=minDiff;
    // remove excessPerColumn from every column we've determined we can remove width from
    for (colIndex = 0; colIndex<mNumCols; colIndex++)
    {
      if ((PR_TRUE==IsColumnInList(colIndex, colsToShrink, numColsToShrink)))
      {
        nscoord colWidth = mTableFrame->GetColumnWidth(colIndex);
        colWidth -= excessPerColumn;
        mTableFrame->SetColumnWidth(colIndex, colWidth);
        excess -= excessPerColumn;
        if (0==excess)
          break;
      }
    }
  } // end while (0<excess)

  if (PR_TRUE==gsDebug)
  {
    nscoord tableWidth=0;
    printf("after AdjustTableThatIsTooWide: ");
    for (PRInt32 i=0; i<mNumCols; i++) 
    {
      tableWidth += mTableFrame->GetColumnWidth(i);
      printf(" %d ", mTableFrame->GetColumnWidth(i));
    }
    printf ("\n  computed table width is %d with aShrinkFixedCols = %s\n",
            tableWidth, aShrinkFixedCols ? "TRUE" : "FALSE");
  }
  delete [] colsToShrink;

  // deal with any excess left over 
  if ((PR_FALSE==aShrinkFixedCols) && (0!=excess))
  {
    // if there's any excess left, we know we've shrunk every non-fixed column to its min
    // so we have to shrink fixed width columns if possible
    AdjustTableThatIsTooWide(aComputedWidth, aTableWidth, PR_TRUE);
  }
  // otherwise we've shrunk the table to its min, and that's all we can do

}


void BasicTableLayoutStrategy::AdjustTableThatIsTooNarrow(nscoord aComputedWidth, 
                                                          nscoord aTableWidth)
{
  if (PR_TRUE==gsDebug)
  {
    nscoord tableWidth=0;
    printf("before AdjustTableThatIsTooNarrow: ");
    for (PRInt32 i=0; i<mNumCols; i++) 
    {
      tableWidth += mTableFrame->GetColumnWidth(i);
      printf(" %d ", mTableFrame->GetColumnWidth(i));
    }
    printf ("\n  computed table width is %d\n",tableWidth);
  }

  PRInt32 numFixedColumns=0;
  PRInt32 *fixedColumns=nsnull;
  mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedColumns, fixedColumns);
  nscoord excess = aTableWidth - aComputedWidth;
  while (0<excess)
  {
    PRInt32 colIndex;
    PRInt32 * colsToGrow = new PRInt32[mNumCols];
    // determine what columns we can take add width to
    PRInt32 numColsToGrow = 0;
    PRBool expandFixedCols = PRBool(mNumCols==numFixedColumns);
    for (colIndex=0; colIndex<mNumCols; colIndex++)
    {
      if ((PR_FALSE==expandFixedCols) && 
          (PR_TRUE==IsColumnInList(colIndex, fixedColumns, numFixedColumns)))
          // skip fixed-width cells if we're told to
        continue;
      if (PR_TRUE==ColIsSpecifiedAsMinimumWidth(colIndex))
        // skip columns that are forced by their attributes to be their minimum width
        continue;
      colsToGrow[numColsToGrow] = colIndex;
      numColsToGrow++;
    }    
    if (0!=numColsToGrow)
    {
      nscoord excessPerColumn;
      if (excess<numColsToGrow)
        excessPerColumn=1;
      else
        excessPerColumn = excess/numColsToGrow;  
      for (colIndex = 0; colIndex<mNumCols; colIndex++)
      {
        if ((PR_TRUE==IsColumnInList(colIndex, colsToGrow, numColsToGrow)))
        {
          nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
          nscoord colWidth = mTableFrame->GetColumnWidth(colIndex);
          colWidth += excessPerColumn;
          if (colWidth > colFrame->GetMinColWidth())
          {
            excess -= excessPerColumn;
            mTableFrame->SetColumnWidth(colIndex, colWidth);
          }
          else
          {
            excess -= mTableFrame->GetColumnWidth(colIndex) - colFrame->GetMinColWidth();   
            mTableFrame->SetColumnWidth(colIndex, colFrame->GetMinColWidth());
          }
          if (0>=excess)
            break;
        }
      }
    }
    delete [] colsToGrow;
    if (0==numColsToGrow)
      break;
  } // end while (0<excess)
  if (PR_TRUE==gsDebug)
  {
    nscoord tableWidth=0;
    printf("after AdjustTableThatIsTooNarrow: ");
    for (PRInt32 i=0; i<mNumCols; i++) 
    {
      tableWidth += mTableFrame->GetColumnWidth(i);
      printf(" %d ", mTableFrame->GetColumnWidth(i));
    }
    printf ("\n  computed table width is %d\n",tableWidth);
  }
}

PRBool BasicTableLayoutStrategy::IsColumnInList(const PRInt32 colIndex, 
                                                PRInt32 *colIndexes, 
                                                PRInt32 aNumFixedColumns)
{
  PRBool result = PR_FALSE;
  for (PRInt32 i=0; i<aNumFixedColumns; i++)
  {
    if (colIndex==colIndexes[i])
    {
      result = PR_TRUE;
      break;
    }
    else if (colIndex<colIndexes[i])
      break;
  }
  return result;
}

PRBool BasicTableLayoutStrategy::ColIsSpecifiedAsMinimumWidth(PRInt32 aColIndex)
{
  PRBool result = PR_FALSE;
  nsTableColFrame* colFrame;
  mTableFrame->GetColumnFrame(aColIndex, colFrame);
  const nsStylePosition* colPosition;
  colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
  switch (colPosition->mWidth.GetUnit())
  {
  case eStyleUnit_Coord:
    if (0==colPosition->mWidth.GetCoordValue())
      result = PR_TRUE;
    break;
  case eStyleUnit_Percent:
    {
      // total hack for now for 0% and 1% specifications
      // should compare percent to available parent width and see that it is below minimum
      // for this column
      float percent = colPosition->mWidth.GetPercentValue();
      if (0.0f == percent || 0.01f == percent)  
        result = PR_TRUE;
      break;
    }
  case eStyleUnit_Proportional:
    if (0==colPosition->mWidth.GetIntValue())
      result=PR_TRUE;
  }

  return result;
}

// returns a list and count of all columns that behave like they have width=auto
// this includes columns with no width specified, and columns whose fixed width comes from a span
void BasicTableLayoutStrategy::GetColumnsThatActLikeAutoWidth(PRInt32 &aNumCols,  PRInt32 *&aColList)
{
  // initialize out params
  aNumCols=0;
  aColList=nsnull;

  // get the auto columns
  PRInt32 numAutoCols=0;
  PRInt32 *autoColList=nsnull;
  mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoCols, autoColList);

  // XXX: have to do next step for %-width as well?

  // get the fixed columns 
  PRInt32 numFixedCols=0;
  PRInt32 *fixedColList=nsnull;
  mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedCols, fixedColList);

  if (0<numAutoCols+numFixedCols)
  {
    // allocate the out param aColList to the biggest it could be (usually a little wasteful, but it's 
    // temp space and the numbers here are small, even for very big tables
    aColList = new PRInt32[numAutoCols+numFixedCols];

    // transfer the auto columns to aColList
    PRInt32 i;
    for (i=0; i<numAutoCols; i++)
    {
      aColList[i]=autoColList[i];
      aNumCols++;
    }

    // get the columns whose fixed width is due to a colspan and transfer them to aColList
    for (i=0; i<numFixedCols; i++)
    {
      nsTableColFrame *colFrame;
      mTableFrame->GetColumnFrame(fixedColList[i], colFrame);
      if (nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN==colFrame->GetWidthSource())
      {
        aColList[aNumCols] = fixedColList[i];
        aNumCols++;
      }
    }
  }

  return;
}


