Bug 368504: Create and use DistributeWidthToColumns for colspan-width and table-width distribution. r+sr=dbaron a=blocking1.9+
authordholbert@cs.stanford.edu
Thu, 17 Jan 2008 20:18:21 -0800
changeset 10378 213ed838e5b72dbf845bf7b60fb02049cca149f3
parent 10377 9c79410fb22d0e479e3e4d1b92cf0ed4281f4c7c
child 10379 5938323c5cbd3ebfbdecdf5f1a3fa3c25fa84488
push idunknown
push userunknown
push dateunknown
reviewersblocking1
bugs368504
milestone1.9b3pre
Bug 368504: Create and use DistributeWidthToColumns for colspan-width and table-width distribution. r+sr=dbaron a=blocking1.9+
layout/reftests/bugs/368504-6-ref.html
layout/reftests/bugs/368504-6.html
layout/reftests/bugs/reftest.list
layout/tables/BasicTableLayoutStrategy.cpp
layout/tables/BasicTableLayoutStrategy.h
layout/tables/nsTableColFrame.cpp
layout/tables/nsTableColFrame.h
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/368504-6-ref.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+ xxx
+ <table style="width: 600px" cellspacing=0 cellpadding=0>
+   <tr>
+     <td style="background: lightgreen" colspan=2>x x x x x x x x x x x x </td>
+     <td style="background: gray">xxx</td>
+   </tr>
+   <tr>
+     <td style="background: orange"   >xxx</td>
+     <td style="background: lightblue">xxx</td>
+     <td style="background: yellow"   >xxx</td>
+   </tr>
+ </table>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/368504-6.html
@@ -0,0 +1,19 @@
+<html>
+<body>
+ <table style="width: 600px" cellspacing=0 cellpadding=0>
+   <tr>
+     <td style="width: 1px" colspan=2>xxx</td>
+     <td class="b">&nbsp</td>
+   </tr>
+   <tr>
+     <td style="background: lightgreen" colspan=2>x x x x x x x x x x x x </td>
+     <td style="background: gray">xxx</td>
+   </tr>
+   <tr>
+     <td style="background: orange"   >xxx</td>
+     <td style="background: lightblue">xxx</td>
+     <td style="background: yellow"   >xxx</td>
+   </tr>
+ </table>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -378,22 +378,23 @@ fails == 368020-3.html 368020-3-ref.html
 fails == 368020-4.html 368020-4-ref.html # bug 368085
 random-if(MOZ_WIDGET_TOOLKIT=="gtk2") == 368020-5.html 368020-5-ref.html # bug 388591
 == 368155-1.xhtml 368155-1-ref.xhtml
 == 368155-negative-margins-1.html 368155-negative-margins-1-ref.html
 # we can't test this because there's antialiasing involved, and our comparison
 # is too exact
 # == 368247-1.html 368247-1-ref.html
 == 368247-2.html 368247-2-ref.html
-fails == 368504-1.html 368504-1-ref.html
-fails == 368504-2.html 368504-2-ref.html
-fails == 368504-3a.html 368504-3-ref.html
-fails == 368504-3b.html 368504-3-ref.html
-fails == 368504-4.html 368504-4-ref.html
-fails == 368504-5.html 368504-5-ref.html
+== 368504-1.html 368504-1-ref.html
+== 368504-2.html 368504-2-ref.html
+== 368504-3a.html 368504-3-ref.html
+== 368504-3b.html 368504-3-ref.html
+== 368504-4.html 368504-4-ref.html
+== 368504-5.html 368504-5-ref.html
+== 368504-6.html 368504-6-ref.html
 == 368622-1.html 368622-1-ref.html
 == 368622-1.html 368622-1-ref.html
 == 368651-1.html 368651-1-ref.html
 == 369975-1.html 369975-1.html
 == 369882.xul 369882-ref.xul
 == 370353-1.html 370353-1-ref.html
 == 370422-1.html 370422-1-ref.html
 == 370525-1.html 370525-1-ref.html
--- a/layout/tables/BasicTableLayoutStrategy.cpp
+++ b/layout/tables/BasicTableLayoutStrategy.cpp
@@ -249,17 +249,16 @@ GetColWidthInfo(nsIRenderingContext *aRe
  * browsers are).
  */
 void
 BasicTableLayoutStrategy::ComputeColumnIntrinsicWidths(nsIRenderingContext* aRenderingContext)
 {
     nsTableFrame *tableFrame = mTableFrame;
     nsTableCellMap *cellMap = tableFrame->GetCellMap();
 
-    nscoord spacing = tableFrame->GetCellSpacingX();
     SpanningCellSorter spanningCells(tableFrame->PresContext()->PresShell());
 
     // Loop over the columns to consider the columns and cells *without*
     // a colspan.
     PRInt32 col, col_end;
     for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
         nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
         if (!colFrame) {
@@ -346,201 +345,24 @@ BasicTableLayoutStrategy::ComputeColumnI
             NS_ASSERTION(cellData && cellData->IsOrig(),
                          "bogus result from spanning cell sorter");
 
             nsTableCellFrame *cellFrame = cellData->GetCellFrame();
             NS_ASSERTION(cellFrame, "bogus result from spanning cell sorter");
 
             CellWidthInfo info = GetCellWidthInfo(aRenderingContext, cellFrame);
 
-            // Before looping over the spanned columns to distribute
-            // this cell's width over the columns it spans, we first
-            // compute totals over the spanned columns so we know how to
-            // allocate the space.
-
-            // Accumulate information about the spanned columns, and
-            // subtract the already-used space from |info|.
-            nscoord totalSPref = 0, totalSMin = 0; // total existing widths
-            nscoord totalSNonPctPref = 0; // total pref width of columns
-                                          // without percentage widths
-            nscoord totalSAutoPref = 0; // total pref width of auto-width cols
-            PRInt32 nonPctCount = 0; // # of columns without percentage widths
-            PRInt32 scol, scol_end;
-            for (scol = col, scol_end = col + colSpan;
-                 scol < scol_end; ++scol) {
-                nsTableColFrame *scolFrame = tableFrame->GetColFrame(scol);
-                if (!scolFrame) {
-                    NS_ERROR("column frames out of sync with cell map");
-                    continue;
-                }
-
-                if (mTableFrame->GetNumCellsOriginatingInCol(scol) &&
-                    scol != col) {
-                    info.minCoord -= spacing;
-                    info.prefCoord = NSCoordSaturatingSubtract(info.prefCoord,
-                                                               spacing,
-                                                               nscoord_MAX);
-                }
-
-                totalSPref += scolFrame->GetPrefCoord();
-                totalSMin += scolFrame->GetMinCoord();
-                if (!scolFrame->GetHasSpecifiedCoord()) {
-                    totalSAutoPref += scolFrame->GetPrefCoord();
-                }
-                float scolPct = scolFrame->GetPrefPercent();
-                if (scolPct == 0.0f) {
-                    totalSNonPctPref += scolFrame->GetPrefCoord();
-                    ++nonPctCount;
-                } else {
-                    info.prefPercent -= scolPct;
-                }
-                info.minCoord -= scolFrame->GetMinCoord();
-                info.prefCoord = 
-                    NSCoordSaturatingSubtract(info.prefCoord,
-                                              scolFrame->GetPrefCoord(),
-                                              nscoord_MAX);
+            if (info.prefPercent > 0.0f) {
+                DistributePctWidthToColumns(info.prefPercent,
+                                            col, colSpan);
             }
-
-            if (info.minCoord < 0)
-                info.minCoord = 0;
-            if (info.prefCoord < 0)
-                info.prefCoord = 0;
-            if (info.prefPercent < 0.0f)
-                info.prefPercent = 0.0f;
-
-            // The min-width of this cell that fits inside the
-            // pref-width of the spanned columns gets distributed
-            // according to different ratios.
-            nscoord minWithinPref =
-                PR_MIN(info.minCoord, totalSPref - totalSMin);
-            NS_ASSERTION(minWithinPref >= 0, "neither value can be negative");
-            nscoord minOutsidePref = info.minCoord - minWithinPref;
-
-            // Loop invariants (that we might get confused about as we
-            // subtract amounts for completed columns)
-            const PRBool spanHasNonPctPref = totalSNonPctPref > 0;
-            const PRBool spanHasPref = totalSPref > 0;
-            const PRBool spanHasNonPct = nonPctCount > 0;
-
-            // ... and actually do the distribution of the widths of
-            // this cell exceeding the totals already in the spanned
-            // columns.
-            for (scol = col, scol_end = col + colSpan;
-                 scol < scol_end; ++scol) {
-                nsTableColFrame *scolFrame = tableFrame->GetColFrame(scol);
-                if (!scolFrame) {
-                    NS_ERROR("column frames out of sync with cell map");
-                    continue;
-                }
-
-                // the percentage width (only to columns that don't
-                // already have percentage widths, in proportion to
-                // the existing pref widths)
-                float allocatedPct = 0.0f;
-                if (scolFrame->GetPrefPercent() == 0.0f &&
-                    info.prefPercent != 0.0f) {
-                    NS_ASSERTION((!spanHasNonPctPref ||
-                                  totalSNonPctPref != 0) &&
-                                 nonPctCount != 0,
-                                 "should not be zero if we haven't allocated "
-                                 "all pref percent");
-                    if (spanHasNonPctPref) {
-                        // Group so we're multiplying by 1.0f when we need
-                        // to use up info.prefPercent.
-                        allocatedPct = info.prefPercent *
-                                           (float(scolFrame->GetPrefCoord()) /
-                                            float(totalSNonPctPref));
-                    } else {
-                        // distribute equally when all pref widths are 0
-                        allocatedPct = info.prefPercent / float(nonPctCount);
-                    }
-                    scolFrame->AddSpanPrefPercent(allocatedPct);
-                }
-
-                // the part of the min width that fits within the
-                // existing pref width
-                float minRatio = 0.0f;
-                if (minWithinPref > 0) {
-                    minRatio = float(scolFrame->GetPrefCoord() -
-                                     scolFrame->GetMinCoord()) /
-                               float(totalSPref - totalSMin);
-                }
-
-                // the rest of the min width, and the pref width (in
-                // proportion to the existing pref widths)
-                float coordRatio; // for both min and pref
-                if (spanHasPref) {
-                    if (scolFrame->GetPrefCoord() == 0) {
-                        // We might have already subtracted all of
-                        // totalSPref.
-                        coordRatio = 0.0f;
-                    } else if (totalSAutoPref == 0) {
-                        // No auto-width cols left -- dividing up totalSPref
-                        coordRatio = float(scolFrame->GetPrefCoord()) /
-                                     float(totalSPref);
-                    } else if (!scolFrame->GetHasSpecifiedCoord()) {
-                        // There are auto-width cols left, and this is one
-                        coordRatio = float(scolFrame->GetPrefCoord()) /
-                                     float(totalSAutoPref);
-                    } else {
-                        // There are auto-width cols left, and this isn't one
-                        coordRatio = 0.0f;
-                    }
-                } else {
-                    // distribute equally when all pref widths are 0
-                    coordRatio = 1.0f / float(scol_end - scol);
-                }
-
-                // combine the two min-width distributions, and record
-                // min and pref
-                nscoord allocatedMinWithinPref =
-                    NSToCoordRound(float(minWithinPref) * minRatio);
-                nscoord allocatedMinOutsidePref =
-                    NSToCoordRound(float(minOutsidePref) * coordRatio);
-                nscoord allocatedPref = 
-                    (info.prefCoord == nscoord_MAX ? 
-                     nscoord_MAX : 
-                     NSToCoordRound(float(info.prefCoord) * coordRatio));
-                nscoord spanMin = scolFrame->GetMinCoord() +
-                        allocatedMinWithinPref + allocatedMinOutsidePref;
-                nscoord spanPref = 
-                    NSCoordSaturatingAdd(scolFrame->GetPrefCoord(),
-                                         allocatedPref);
-                scolFrame->AddSpanCoords(spanMin, spanPref,
-                                         info.hasSpecifiedWidth);
-
-                // To avoid accumulating rounding error from division,
-                // subtract everything to do with the column we've
-                // passed from the totals.
-                minWithinPref -= allocatedMinWithinPref;
-                minOutsidePref -= allocatedMinOutsidePref;
-                info.prefCoord = NSCoordSaturatingSubtract(info.prefCoord, 
-                                                           allocatedPref,
-                                                           nscoord_MAX);
-                info.prefPercent -= allocatedPct;
-                totalSPref -= scolFrame->GetPrefCoord();
-                totalSMin -= scolFrame->GetMinCoord();
-                if (!scolFrame->GetHasSpecifiedCoord()) {
-                    totalSAutoPref -= scolFrame->GetPrefCoord();
-                }                
-                if (scolFrame->GetPrefPercent() == 0.0f) {
-                    totalSNonPctPref -= scolFrame->GetPrefCoord();
-                    --nonPctCount;
-                }
-            }
-
-            // Note that we only distribute the percentage if
-            // spanHasNonPct.
-            NS_ASSERTION(totalSPref == 0 && totalSMin == 0 &&
-                         totalSNonPctPref == 0 && nonPctCount == 0 &&
-                         minOutsidePref == 0 && minWithinPref == 0 &&
-                         (info.prefCoord == 0 || 
-                          info.prefCoord == nscoord_MAX) &&
-                         (info.prefPercent == 0.0f || !spanHasNonPct),
-                         "didn't subtract all that we added");
+            DistributeWidthToColumns(info.minCoord, col, colSpan, 
+                                     BTLS_MIN_WIDTH, info.hasSpecifiedWidth);
+            DistributeWidthToColumns(info.prefCoord, col, colSpan, 
+                                     BTLS_PREF_WIDTH, info.hasSpecifiedWidth);
         } while ((item = item->next));
 
         // Combine the results of the span analysis into the main results,
         // for each increment of colspan.
 
         for (col = 0, col_end = cellMap->GetColCount(); col < col_end; ++col) {
             nsTableColFrame *colFrame = tableFrame->GetColFrame(col);
             if (!colFrame) {
@@ -694,35 +516,138 @@ BasicTableLayoutStrategy::ComputeColumnW
     if (mMinWidth == NS_INTRINSIC_WIDTH_UNKNOWN)
         ComputeIntrinsicWidths(aReflowState.rendContext);
 
     nsTableCellMap *cellMap = mTableFrame->GetCellMap();
     PRInt32 colCount = cellMap->GetColCount();
     if (colCount <= 0)
         return; // nothing to do
 
-    nscoord spacing = mTableFrame->GetCellSpacingX();
+    DistributeWidthToColumns(width, 0, colCount, BTLS_FINAL_WIDTH, PR_FALSE);
+
+#ifdef DEBUG_TABLE_STRATEGY
+    printf("ComputeColumnWidths final\n");
+    mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
+#endif
+}
+
+void
+BasicTableLayoutStrategy::DistributePctWidthToColumns(float aSpanPrefPct,
+                                                      PRInt32 aFirstCol,
+                                                      PRInt32 aColCount)
+{
+    // First loop to determine:
+    PRInt32 nonPctColCount = 0; // number of spanned columns without % width
+    nscoord nonPctTotalPrefWidth = 0; // total pref width of those columns
+    // and to reduce aSpanPrefPct by columns that already have % width
+
+    PRInt32 scol, scol_end;
+    for (scol = aFirstCol, scol_end = aFirstCol + aColCount;
+         scol < scol_end; ++scol) {
+        nsTableColFrame *scolFrame = mTableFrame->GetColFrame(scol);
+        if (!scolFrame) {
+            NS_ERROR("column frames out of sync with cell map");
+            continue;
+        }
+        float scolPct = scolFrame->GetPrefPercent();
+        if (scolPct == 0.0f) {
+            nonPctTotalPrefWidth += scolFrame->GetPrefCoord();
+            ++nonPctColCount;
+        } else {
+            aSpanPrefPct -= scolPct;
+        }
+    }
+
+    if (aSpanPrefPct <= 0.0f || nonPctColCount == 0) {
+        // There's no %-width on the colspan left over to distribute,
+        // or there are no columns to which we could distribute %-width
+        return;
+    }
 
-    nscoord min = mMinWidth;
+    // Second loop, to distribute what remains of aSpanPrefPct
+    // between the non-percent-width spanned columns
+    const PRBool spanHasNonPctPref = nonPctTotalPrefWidth > 0; // Loop invariant
+    for (scol = aFirstCol, scol_end = aFirstCol + aColCount;
+         scol < scol_end; ++scol) {
+        nsTableColFrame *scolFrame = mTableFrame->GetColFrame(scol);
+        if (!scolFrame) {
+            NS_ERROR("column frames out of sync with cell map");
+            continue;
+        }
+
+        // the percentage width (only to columns that don't
+        // already have percentage widths, in proportion to
+        // the existing pref widths)
+        float allocatedPct = 0.0f;
+        if (scolFrame->GetPrefPercent() == 0.0f) {
+            NS_ASSERTION((!spanHasNonPctPref ||
+                          nonPctTotalPrefWidth != 0) &&
+                         nonPctColCount != 0,
+                         "should not be zero if we haven't allocated "
+                         "all pref percent");
+            if (spanHasNonPctPref) {
+                // Group so we're multiplying by 1.0f when we need
+                // to use up aSpanPrefPct.
+                allocatedPct = aSpanPrefPct *
+                    (float(scolFrame->GetPrefCoord()) /
+                     float(nonPctTotalPrefWidth));
+            } else {
+                // distribute equally when all pref widths are 0
+                allocatedPct = aSpanPrefPct / float(nonPctColCount);
+            }
+            // Allocate the percent
+            scolFrame->AddSpanPrefPercent(allocatedPct);
+            
+            // To avoid accumulating rounding error from division,
+            // subtract this column's values from the totals.
+            aSpanPrefPct -= allocatedPct;
+            nonPctTotalPrefWidth -= scolFrame->GetPrefCoord();
+            --nonPctColCount;
+        }
+    }
+}
+
+void
+BasicTableLayoutStrategy::DistributeWidthToColumns(nscoord aWidth, 
+                                                   PRInt32 aFirstCol, 
+                                                   PRInt32 aColCount,
+                                                   BtlsWidthType aWidthType,
+                                                   PRBool aSpanHasSpecifiedWidth)
+{
+    NS_ASSERTION(aWidthType != BTLS_FINAL_WIDTH || 
+                 (aFirstCol == 0 && 
+                  aColCount == mTableFrame->GetCellMap()->GetColCount()),
+            "Computing final column widths, but didn't get full column range");
 
     // border-spacing isn't part of the basis for percentages.
-    nscoord subtract = spacing;
-    for (PRInt32 col = 0; col < colCount; ++col) {
+    nscoord spacing = mTableFrame->GetCellSpacingX();
+    nscoord subtract = 0;    
+    // aWidth initially includes border-spacing for the boundaries in between
+    // each of the columns. We start at aFirstCol + 1 because the first
+    // in-between boundary would be at the left edge of column aFirstCol + 1
+    for (PRInt32 col = aFirstCol + 1; col < aFirstCol + aColCount; ++col) {
         if (mTableFrame->GetNumCellsOriginatingInCol(col)) {
             subtract += spacing;
         }
     }
-    width = NSCoordSaturatingSubtract(width, subtract, nscoord_MAX);
-    min -= subtract;
-
-    // XXX is |width| the right basis for percentage widths?
+    if (aWidthType == BTLS_FINAL_WIDTH) {
+        // If we're computing final col-width, then aWidth initially includes
+        // border spacing on the table's far left + far right edge, too.  Need
+        // to subtract those out, too.
+        subtract += spacing * 2;
+    }
+    aWidth = NSCoordSaturatingSubtract(aWidth, subtract, nscoord_MAX);
 
     /*
-     * The goal of this function is to allocate |width| to the columns
-     * by making an appropriate SetFinalWidth call to each column.
+     * The goal of this function is to distribute |aWidth| between the
+     * columns by making an appropriate AddSpanCoords or SetFinalWidth
+     * call for each column.  (We call AddSpanCoords if we're 
+     * distributing a column-spanning cell's minimum or preferred width
+     * to its spanned columns.  We call SetFinalWidth if we're 
+     * distributing a table's final width to its columns.)
      *
      * The idea is to either assign one of the following sets of widths
      * or a weighted average of two adjacent sets of widths.  It is not
      * possible to assign values smaller than the smallest set of
      * widths.  However, see below for handling the case of assigning
      * values larger than the largest set of widths.  From smallest to
      * largest, these are:
      *
@@ -735,17 +660,17 @@ BasicTableLayoutStrategy::ComputeColumnW
      * their percentage width, all columns with specified coordinate
      * widths their pref width (since it doesn't matter whether it's the
      * largest contributor to the pref width that was the specified
      * contributor), and all other columns their min width.
      *
      * 4. [guess_pref] Assign all columns with percentage widths their
      * specified width, and all other columns their pref width.
      *
-     * If |width| is *larger* than what we would assign in (4), then we
+     * If |aWidth| is *larger* than what we would assign in (4), then we
      * expand the columns:
      *
      *   a. if any columns without a specified coordinate width or
      *   percent width have nonzero pref width, in proportion to pref
      *   width [total_flex_pref]
      *
      *   b. otherwise, if any columns without percent width have nonzero
      *   pref width, in proportion to pref width [total_fixed_pref]
@@ -764,28 +689,28 @@ BasicTableLayoutStrategy::ComputeColumnW
             guess_min_spec = 0,
             guess_pref = 0,
             total_flex_pref = 0,
             total_fixed_pref = 0;
     float total_pct = 0.0f; // 0.0f to 1.0f
     PRInt32 numInfiniteWidthCols = 0;
 
     PRInt32 col;
-    for (col = 0; col < colCount; ++col) {
+    for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
         nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
         if (!colFrame) {
             NS_ERROR("column frames out of sync with cell map");
             continue;
         }
         nscoord min_width = colFrame->GetMinCoord();
         guess_min += min_width;
         if (colFrame->GetPrefPercent() != 0.0f) {
             float pct = colFrame->GetPrefPercent();
             total_pct += pct;
-            nscoord val = nscoord(float(width) * pct);
+            nscoord val = nscoord(float(aWidth) * pct);
             if (val < min_width)
                 val = min_width;
             guess_min_pct += val;
             guess_pref = NSCoordSaturatingAdd(guess_pref, val);
         } else {
             nscoord pref_width = colFrame->GetPrefCoord();
             if (pref_width == nscoord_MAX) {
                 numInfiniteWidthCols++;
@@ -808,104 +733,108 @@ BasicTableLayoutStrategy::ComputeColumnW
     }
     guess_min_spec = NSCoordSaturatingAdd(guess_min_spec, guess_min_pct);
 
     // Determine what we're flexing:
     enum Loop2Type {
         FLEX_PCT_SMALL, // between (1) and (2) above
         FLEX_FIXED_SMALL, // between (2) and (3) above
         FLEX_FLEX_SMALL, // between (3) and (4) above
-        FLEX_FLEX_LARGE, // above (4) above, case (a)
-        FLEX_FIXED_LARGE, // above (4) above, case (b)
-        FLEX_PCT_LARGE, // above (4) above, case (c)
-        FLEX_ALL_LARGE // above (4) above, case (d)
+        FLEX_FLEX_LARGE, // greater than (4) above, case (a)
+        FLEX_FIXED_LARGE, // greater than (4) above, case (b)
+        FLEX_PCT_LARGE, // greater than (4) above, case (c)
+        FLEX_ALL_LARGE // greater than (4) above, case (d)
     };
 
     Loop2Type l2t;
     // These are constants (over columns) for each case's math.  We use
     // a pair of nscoords rather than a float so that we can subtract
     // each column's allocation so we avoid accumulating rounding error.
     nscoord space; // the amount of extra width to allocate
     union {
         nscoord c;
         float f;
     } basis; // the sum of the statistic over columns to divide it
-    if (width < guess_pref) {
-        NS_ASSERTION(width >= guess_min, "bad width");
-        if (width < guess_min_pct) {
+    if (aWidth < guess_pref) {
+        if ((aWidthType == BTLS_MIN_WIDTH  && aWidth <= guess_min) ||
+            (aWidthType == BTLS_PREF_WIDTH && aWidth <= guess_pref)) {
+            // Return early -- we don't have any extra space to distribute.
+            return;
+        }
+        NS_ASSERTION(!(aWidthType == BTLS_FINAL_WIDTH && aWidth < guess_min),
+                     "Table width is less than the "
+                     "sum of its columns' min widths");
+        if (aWidth < guess_min_pct) {
             l2t = FLEX_PCT_SMALL;
-            space = width - guess_min;
+            space = aWidth - guess_min;
             basis.c = guess_min_pct - guess_min;
-        } else if (width < guess_min_spec) {
+        } else if (aWidth < guess_min_spec) {
             l2t = FLEX_FIXED_SMALL;
-            space = width - guess_min_pct;
+            space = aWidth - guess_min_pct;
             basis.c = NSCoordSaturatingSubtract(guess_min_spec, guess_min_pct,
                                                 nscoord_MAX);
         } else {
             l2t = FLEX_FLEX_SMALL;
-            space = width - guess_min_spec;
+            space = aWidth - guess_min_spec;
             basis.c = NSCoordSaturatingSubtract(guess_pref, guess_min_spec,
                                                 nscoord_MAX);
         }
     } else {
-        // Note: Shouldn't have to check for nscoord_MAX in this case, because
-        // width should be much less than nscoord_MAX, and being here means
-        // guess_pref is no larger than width.
-        space = width - guess_pref;
+        space = NSCoordSaturatingSubtract(aWidth, guess_pref, nscoord_MAX);
         if (total_flex_pref > 0) {
             l2t = FLEX_FLEX_LARGE;
             basis.c = total_flex_pref;
         } else if (total_fixed_pref > 0) {
             l2t = FLEX_FIXED_LARGE;
             basis.c = total_fixed_pref;
         } else if (total_pct > 0.0f) {
             l2t = FLEX_PCT_LARGE;
             basis.f = total_pct;
         } else {
             l2t = FLEX_ALL_LARGE;
-            basis.c = colCount;
+            basis.c = aColCount;
         }
     }
 
 #ifdef DEBUG_dbaron_off
     printf("ComputeColumnWidths: %d columns in width %d,\n"
            "  guesses=[%d,%d,%d,%d], totals=[%d,%d,%f],\n"
            "  l2t=%d, space=%d, basis.c=%d\n",
-           colCount, width,
+           aColCount, aWidth,
            guess_min, guess_min_pct, guess_min_spec, guess_pref,
            total_flex_pref, total_fixed_pref, total_pct,
            l2t, space, basis.c);
 #endif
 
-    for (col = 0; col < colCount; ++col) {
+    for (col = aFirstCol; col < aFirstCol + aColCount; ++col) {
         nsTableColFrame *colFrame = mTableFrame->GetColFrame(col);
         if (!colFrame) {
             NS_ERROR("column frames out of sync with cell map");
             continue;
         }
         nscoord col_width;
 
         float pct = colFrame->GetPrefPercent();
         if (pct != 0.0f) {
-            col_width = nscoord(float(width) * pct);
+            col_width = nscoord(float(aWidth) * pct);
             nscoord col_min = colFrame->GetMinCoord();
             if (col_width < col_min)
                 col_width = col_min;
         } else {
             col_width = colFrame->GetPrefCoord();
         }
 
         nscoord col_width_before_adjust = col_width;
 
         switch (l2t) {
             case FLEX_PCT_SMALL:
                 col_width = col_width_before_adjust = colFrame->GetMinCoord();
                 if (pct != 0.0f) {
                     nscoord pct_minus_min =
-                        nscoord(float(width) * pct) - col_width;
+                        nscoord(float(aWidth) * pct) - col_width;
                     if (pct_minus_min > 0) {
                         float c = float(space) / float(basis.c);
                         basis.c -= pct_minus_min;
                         col_width += NSToCoordRound(float(pct_minus_min) * c);
                     }
                 }
                 break;
             case FLEX_FIXED_SMALL:
@@ -963,19 +892,24 @@ BasicTableLayoutStrategy::ComputeColumnW
                 }
                 break;
             case FLEX_FLEX_LARGE:
                 if (pct == 0.0f &&
                     !colFrame->GetHasSpecifiedCoord()) {
                     NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
                                  "wrong width assigned");
                     if (col_width != 0) {
-                        float c = float(space) / float(basis.c);
-                        basis.c -= col_width;
-                        col_width += NSToCoordRound(float(col_width) * c);
+                        if (space == nscoord_MAX) {
+                            basis.c -= col_width;
+                            col_width = nscoord_MAX;
+                        } else {
+                            float c = float(space) / float(basis.c);
+                            basis.c -= col_width;
+                            col_width += NSToCoordRound(float(col_width) * c);
+                        }
                     }
                 }
                 break;
             case FLEX_FIXED_LARGE:
                 if (pct == 0.0f) {
                     NS_ASSERTION(col_width == colFrame->GetPrefCoord(),
                                  "wrong width assigned");
                     NS_ASSERTION(colFrame->GetHasSpecifiedCoord() ||
@@ -1001,29 +935,58 @@ BasicTableLayoutStrategy::ComputeColumnW
                 {
                     float c = float(space) / float(basis.c);
                     col_width += NSToCoordRound(c);
                     --basis.c;
                 }
                 break;
         }
 
-        space -= col_width - col_width_before_adjust;
+        // Only subtract from space if it's a real number.
+        if (space != nscoord_MAX) {
+            NS_ASSERTION(col_width != nscoord_MAX,
+                 "How is col_width nscoord_MAX if space isn't?");
+            NS_ASSERTION(col_width_before_adjust != nscoord_MAX,
+                 "How is col_width_before_adjust nscoord_MAX if space isn't?");
+            space -= col_width - col_width_before_adjust;
+        }
 
         NS_ASSERTION(col_width >= colFrame->GetMinCoord(),
                      "assigned width smaller than min");
-
-        nscoord old_final = colFrame->GetFinalWidth();
-        colFrame->SetFinalWidth(col_width);
-
-        if (old_final != col_width)
-            mTableFrame->DidResizeColumns();
+        
+        // Apply the new width
+        switch (aWidthType) {
+            case BTLS_MIN_WIDTH:
+                {
+                    // Note: AddSpanCoords requires both a min and pref width.
+                    // For the pref width, we'll just pass in our computed
+                    // min width, because the real pref width will be at least
+                    // as big
+                    colFrame->AddSpanCoords(col_width, col_width, 
+                                            aSpanHasSpecifiedWidth);
+                }
+                break;
+            case BTLS_PREF_WIDTH:
+                {
+                    // Note: AddSpanCoords requires both a min and pref width.
+                    // For the min width, we'll just pass in 0, because
+                    // the real min width will be at least 0
+                    colFrame->AddSpanCoords(0, col_width, 
+                                            aSpanHasSpecifiedWidth);
+                }
+                break;
+            case BTLS_FINAL_WIDTH:
+                {
+                    nscoord old_final = colFrame->GetFinalWidth();
+                    colFrame->SetFinalWidth(col_width);
+                    
+                    if (old_final != col_width)
+                        mTableFrame->DidResizeColumns();
+                }
+                break;                
+        }
     }
-    NS_ASSERTION(space == 0 &&
+    NS_ASSERTION((space == 0 || space == nscoord_MAX) &&
                  ((l2t == FLEX_PCT_LARGE)
                     ? (-0.001f < basis.f && basis.f < 0.001f)
                     : (basis.c == 0 || basis.c == nscoord_MAX)),
                  "didn't subtract all that we added");
-#ifdef DEBUG_TABLE_STRATEGY
-    printf("ComputeColumnWidths final\n");
-    mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
-#endif
 }
--- a/layout/tables/BasicTableLayoutStrategy.h
+++ b/layout/tables/BasicTableLayoutStrategy.h
@@ -57,19 +57,52 @@ public:
     // nsITableLayoutStrategy implementation
     virtual nscoord GetMinWidth(nsIRenderingContext* aRenderingContext);
     virtual nscoord GetPrefWidth(nsIRenderingContext* aRenderingContext,
                                  PRBool aComputingSize);
     virtual void MarkIntrinsicWidthsDirty();
     virtual void ComputeColumnWidths(const nsHTMLReflowState& aReflowState);
 
 private:
+    // NOTE: Using prefix "BTLS" to avoid overlapping names with 
+    // the values of nsLayoutUtils::IntrinsicWidthType
+    enum BtlsWidthType { BTLS_MIN_WIDTH, 
+                         BTLS_PREF_WIDTH, 
+                         BTLS_FINAL_WIDTH };
+
     // Compute intrinsic width member variables on the columns.
     void ComputeColumnIntrinsicWidths(nsIRenderingContext* aRenderingContext);
 
+    // Distribute a colspanning cell's percent width (if any) to its columns.
+    void DistributePctWidthToColumns(float aSpanPrefPct,
+                                     PRInt32 aFirstCol,
+                                     PRInt32 aColCount);
+
+    // Distribute a width of some BltsWidthType type to a set of columns.
+    //  aWidth: The amount of width to be distributed
+    //  aFirstCol: The index (in the table) of the first column to be
+    //             considered for receiving width
+    //  aColCount: The number of consecutive columns (starting with aFirstCol)
+    //             to be considered for receiving width
+    //  aWidthType: The type of width being distributed.  (BTLS_MIN_WIDTH and
+    //              BTLS_PREF_WIDTH are intended to be used for dividing up
+    //              colspan's min & pref width.  BTLS_FINAL_WIDTH is intended
+    //              to be used for distributing the table's final width across
+    //              all its columns)
+    //  aSpanHasSpecifiedWidth: Should be PR_TRUE iff:
+    //                           - We're distributing a colspanning cell's
+    //                             pref or min width to its columns
+    //                           - The colspanning cell has a specified width.
+    void DistributeWidthToColumns(nscoord aWidth, 
+                                  PRInt32 aFirstCol, 
+                                  PRInt32 aColCount,
+                                  BtlsWidthType aWidthType,
+                                  PRBool aSpanHasSpecifiedWidth);
+ 
+
     // Compute the min and pref widths of the table from the width
     // variables on the columns.
     void ComputeIntrinsicWidths(nsIRenderingContext* aRenderingContext);
 
     nsTableFrame *mTableFrame;
     nscoord mMinWidth;
     nscoord mPrefWidth;
     nscoord mPrefWidthPctExpand;
--- a/layout/tables/nsTableColFrame.cpp
+++ b/layout/tables/nsTableColFrame.cpp
@@ -150,19 +150,19 @@ void nsTableColFrame::Dump(PRInt32 aInde
     break;
   case eColAnonymousColGroup:
     printf(" anonymous-colgroup ");
     break;
   case eColAnonymousCell: 
     printf(" anonymous-cell ");
     break;
   }
-  printf("\nm:%d c:%d(%c) p:%f sm:%d sc:%d(%c) sp:%f f:%d",
+  printf("\nm:%d c:%d(%c) p:%f sm:%d sc:%d sp:%f f:%d",
          mMinCoord, mPrefCoord, mHasSpecifiedCoord ? 's' : 'u', mPrefPercent,
-         mSpanMinCoord, mSpanPrefCoord, mSpanHasSpecifiedCoord ? 's' : 'u',
+         mSpanMinCoord, mSpanPrefCoord,
          mSpanPrefPercent,
          GetFinalWidth());
   printf("\n%s**END COL DUMP** ", indent);
   delete [] indent;
 }
 #endif
 /* ----- global methods ----- */
 
--- a/layout/tables/nsTableColFrame.h
+++ b/layout/tables/nsTableColFrame.h
@@ -160,17 +160,16 @@ public:
   /**
    * Restore the default values of the temporary buffer for
    * spanning-cell intrinsic widths (as we process spanning cells).
    */
   void ResetSpanIntrinsics() {
     mSpanMinCoord = 0;
     mSpanPrefCoord = 0;
     mSpanPrefPercent = 0.0f;
-    mSpanHasSpecifiedCoord = PR_FALSE;
   }
 
   /**
    * Add the widths for a cell or column element, or the contribution of
    * the widths from a column-spanning cell:
    * @param aMinCoord The minimum intrinsic width
    * @param aPrefCoord The preferred intrinsic width or, if there is a
    *   specified non-percentage width, max(specified width, minimum intrinsic
@@ -244,21 +243,17 @@ public:
    * Like AddCoords, but into a temporary buffer used for groups of
    * column-spanning cells.
    */
   void AddSpanCoords(nscoord aSpanMinCoord, nscoord aSpanPrefCoord,
                      PRBool aSpanHasSpecifiedCoord) {
     NS_ASSERTION(aSpanMinCoord <= aSpanPrefCoord,
                  "intrinsic widths out of order");
 
-    if (aSpanHasSpecifiedCoord && !mSpanHasSpecifiedCoord) {
-      mSpanPrefCoord = mSpanMinCoord;
-      mSpanHasSpecifiedCoord = PR_TRUE;
-    }
-    if (!aSpanHasSpecifiedCoord && mSpanHasSpecifiedCoord) {
+    if (!aSpanHasSpecifiedCoord && mHasSpecifiedCoord) {
       aSpanPrefCoord = aSpanMinCoord; // NOTE: modifying argument
     }
 
     if (aSpanMinCoord > mSpanMinCoord)
       mSpanMinCoord = aSpanMinCoord;
     if (aSpanPrefCoord > mSpanPrefCoord)
       mSpanPrefCoord = aSpanPrefCoord;
 
@@ -274,17 +269,17 @@ public:
       mSpanPrefPercent = aSpanPrefPercent;
   }
 
   /*
    * Accumulate the temporary variables for column spanning cells into
    * the primary variables.
    */
   void AccumulateSpanIntrinsics() {
-    AddCoords(mSpanMinCoord, mSpanPrefCoord, mHasSpecifiedCoord && mSpanHasSpecifiedCoord);
+    AddCoords(mSpanMinCoord, mSpanPrefCoord, mHasSpecifiedCoord);
     AddPrefPercent(mSpanPrefPercent);
   }
 
   // Used to adjust a column's pref percent so that the table's total
   // never exceeeds 100% (by only allowing percentages to be used,
   // starting at the first column, until they reach 100%).
   void AdjustPrefPercent(float *aTableTotalPercent) {
     float allowed = 1.0f - *aTableTotalPercent;
@@ -317,17 +312,16 @@ protected:
   // border width in pixels of the inner half of the border only
   BCPixelSize mLeftBorderWidth;
   BCPixelSize mRightBorderWidth;
   BCPixelSize mTopContBorderWidth;
   BCPixelSize mRightContBorderWidth;
   BCPixelSize mBottomContBorderWidth;
 
   PRPackedBool mHasSpecifiedCoord;
-  PRPackedBool mSpanHasSpecifiedCoord; // XXX...
   nscoord mMinCoord;
   nscoord mPrefCoord;
   nscoord mSpanMinCoord; // XXX...
   nscoord mSpanPrefCoord; // XXX...
   float mPrefPercent;
   float mSpanPrefPercent; // XXX...
   // ...XXX the four members marked above could be allocated as part of
   // a separate array allocated only during